mcp-dataverse 0.7.5 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAPABILITIES.md +59 -3
- package/README.md +16 -0
- package/assets/demo-thumbnail.jpg +0 -0
- package/assets/demo.mp4 +0 -0
- package/dist/server.js +44 -21
- package/dist/update-B3OXP6BV.js +30 -0
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/server.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as Te}from"./chunk-OQ46VPYS.js";import{a as Re}from"./chunk-M5UROAVJ.js";import"./chunk-RYRO3QPE.js";import"./chunk-KJ3HM2VM.js";import{a as v,b as Ae}from"./chunk-IA4YDPXM.js";import{Server as br}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as _r}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as wr,ListToolsRequestSchema as Nr,ListResourcesRequestSchema as Sr,ListResourceTemplatesRequestSchema as Tr,ReadResourceRequestSchema as Rr,ListPromptsRequestSchema as Ar,GetPromptRequestSchema as kr}from"@modelcontextprotocol/sdk/types.js";import{readFileSync as Er}from"fs";import{join as Cr,dirname as Dr}from"path";import{fileURLToPath as Ir}from"url";function ke(){let n=process.argv.slice(2),i="stdio",a=3e3;for(let e=0;e<n.length;e++){if(n[e]==="--transport"&&n[e+1]){let t=n[e+1];(t==="http"||t==="stdio")&&(i=t),e++}if(n[e]==="--port"&&n[e+1]){let t=parseInt(n[e+1],10);!isNaN(t)&&t>0&&t<65536&&(a=t),e++}}return{transport:i,port:a}}var ce=class{map=new Map;register(i){for(let a of i.tools){if(this.map.has(a.name))throw new Error(`Duplicate tool name: ${a.name}`);this.map.set(a.name,{definition:a,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 Ee(n){let i=new ce;for(let a of n)i.register(a);return i}var ee=class{server;token;constructor(i,a){this.server=i??null,this.token=a??null}async report(i,a){if(!(!this.server||this.token==null))try{await this.server.notification({method:"notifications/progress",params:{progressToken:this.token,progress:i,total:a}})}catch{}}},qr=new ee;function L(n){let i=n.errorCategory!==void 0,a=i?{...n,isError:!0,content:[{type:"text",text:n.summary}]}:n,e={content:[{type:"text",text:JSON.stringify(a,null,2)}]};return i&&(e.isError=!0),e}function u(n,i,a,e){return L({summary:n,data:i,suggestions:a,warnings:e})}function H(n,i,a){return L({summary:`${i.length} ${n} found.`,data:i,suggestions:a})}function ne(n,i="UNKNOWN",a){return L({summary:n,data:null,errorCategory:i,suggestions:a})}function Y(n){let i={feature_disabled:"ENV_LIMITATION",permission_required:"PERMISSIONS",schema_missing:"SCHEMA_MISMATCH"};return L({summary:`Blocked: ${n.cannotProceedBecause}`,data:null,prerequisite:n,errorCategory:i[n.type]})}var de="@OData.Community.Display.V1.FormattedValue";function V(n){return n.map(i=>{if(typeof i!="object"||i===null)return i;let a=i,e={};for(let r of Object.keys(a))if(r.endsWith(de)){let s=r.slice(0,-de.length);e[s]=a[r]}if(Object.keys(e).length===0)return i;let t={};for(let r of Object.keys(a))r.endsWith(de)||(Object.prototype.hasOwnProperty.call(e,r)?t[r]={value:a[r],label:e[r]}:t[r]=a[r]);return t})}var Ce=[{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 De(n,i,a){if(n==="dataverse_whoami"){let e=await a.whoAmI(),t={userId:e.UserId,businessUnitId:e.BusinessUnitId,organizationId:e.OrganizationId,organizationName:e.OrganizationName,environmentUrl:e.EnvironmentUrl};return u(`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: ${n}`)}import{z as Ue}from"zod";import{z as k}from"zod";var ue=[{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. WHEN TO USE: Discovering which tables exist in the environment. BEST PRACTICES: Start without includeSystemTables to focus on custom tables; use dataverse_get_table_metadata for column details. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{includeSystemTables:{type:"boolean",description:"Include system tables. Default false = custom tables only."}},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. WHEN TO USE: Before building queries or creating/updating records to validate field names, types, and required levels. BEST PRACTICES: Set includeAttributes=false for table-level info only; call once and reuse results. WORKFLOW: explore_schema.",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"}},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. WHEN TO USE: Finding relationship schema names for associate/disassociate, or mapping lookups for FetchXML joins. BEST PRACTICES: Filter by relationshipType to reduce output size. WORKFLOW: explore_schema.",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. WHEN TO USE: Discovering shared option sets reused across multiple tables. BEST PRACTICES: Follow up with dataverse_get_option_set to retrieve individual option values. WORKFLOW: explore_schema.",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. WHEN TO USE: Looking up numeric codes for picklist filtering or record creation. BEST PRACTICES: Use the integer values in $filter expressions, not the label text. WORKFLOW: explore_schema.",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. WHEN TO USE: Before using dataverse_upsert to verify which fields serve as alternate keys. BEST PRACTICES: Check that key index status is Active before relying on a key. WORKFLOW: explore_schema.",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. WHEN TO USE: Looking up option values for a specific table's picklist, multi-select picklist, status, or state column. BEST PRACTICES: Use the integer values in $filter and when setting fields in create/update. WORKFLOW: explore_schema.",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}}],da=k.object({logicalName:k.string().min(1),includeAttributes:k.boolean().optional().default(!0)}),ua=k.object({logicalName:k.string().min(1),relationshipType:k.enum(["OneToMany","ManyToOne","ManyToMany","All"]).optional()}),pa=k.object({includeSystemTables:k.boolean().default(!1).optional().describe("Include system tables. Default false = custom tables only.")}),ma=k.object({name:k.string().min(1)}),ga=k.object({tableName:k.string().min(1).describe('Logical name of the table (e.g., "account", "contact")')}),Ie=/^[a-z_][a-z0-9_]*$/,fa=k.object({entityLogicalName:k.string().min(1).regex(Ie,"Invalid Dataverse logical name"),attributeLogicalName:k.string().min(1).regex(Ie,"Invalid Dataverse logical name")});async function xe(n,i,a){switch(n){case"dataverse_list_tables":{let{includeSystemTables:e=!1}=pa.parse(i??{}),t=!e,r=await a.listTables(t),s=Array.isArray(r)?r:[];return H(`tables (${t?"custom only":"including system"})`,s,["Use dataverse_get_table_metadata to inspect a specific table's columns and types"])}case"dataverse_get_table_metadata":{let{logicalName:e,includeAttributes:t}=da.parse(i),r=await a.getTableMetadata(e,t),s=Array.isArray(r?.Attributes)?r.Attributes.length:0;return u(`Metadata for ${e}: ${s} attributes`,r,["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}=ua.parse(i),r=await a.getRelationships(e),s=e.toLowerCase(),l=r.filter(m=>m.RelationshipType==="OneToManyRelationship"&&m.ReferencedEntity?.toLowerCase()===s),o=r.filter(m=>m.RelationshipType==="OneToManyRelationship"&&m.ReferencingEntity?.toLowerCase()===s),c=r.filter(m=>m.RelationshipType==="ManyToManyRelationship"),d=!t||t==="All",p=[...d||t==="OneToMany"?l:[],...d||t==="ManyToOne"?o:[],...d||t==="ManyToMany"?c:[]];return u(`${p.length} relationships found for ${e}`,{tableName:e,oneToMany:d||t==="OneToMany"?l:void 0,manyToOne:d||t==="ManyToOne"?o:void 0,manyToMany:d||t==="ManyToMany"?c:void 0},["Use dataverse_associate or dataverse_disassociate to manage N:N relationships"])}case"dataverse_list_global_option_sets":{let e=await a.listGlobalOptionSets(),t=Array.isArray(e)?e:[];return H("global option sets",t,["Use dataverse_get_option_set with a name to see the values"])}case"dataverse_get_option_set":{let{name:e}=ma.parse(i),t=await a.getOptionSet(e),r=Array.isArray(t?.Options)?t.Options:[];return u(`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}=ga.parse(i),t=await a.getEntityKeys(e);return u(`${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}=fa.parse(i),r=await a.getAttributeOptionSet(e,t),s=Array.isArray(r?.options)?r.options:[];return u(`Attribute '${t}' on '${e}': ${s.length} options`,r,["Use these integer values in $filter or when creating/updating records"])}default:throw new Error(`Unknown metadata-read tool: ${n}`)}}import{z as K}from"zod";var ya=new Set(["dataverse_delete","dataverse_delete_file","dataverse_update_entity","dataverse_delete_attribute"]);function pe(n){let i=[];return n.top!==void 0&&n.top>5e3&&i.push({code:"LARGE_RESULT_SET",message:`Large result set requested (top=${n.top}). Consider reducing $top or using paging.`,severity:"warning"}),(!n.select||n.select.length===0)&&i.push({code:"NO_SELECT",message:"No $select specified \u2014 all columns will be returned. Specify columns to reduce payload size.",severity:"warning"}),n.filter||i.push({code:"NO_FILTER",message:"No filter \u2014 consider adding one to reduce results and improve performance.",severity:"info"}),i}function $(n){let i=[];return ya.has(n.toolName)&&i.push({code:"DESTRUCTIVE_OP",message:"Destructive operation \u2014 confirm with user before proceeding. This action cannot be undone.",severity:"warning"}),n.toolName==="dataverse_batch_execute"&&i.push({code:"BATCH_OP",message:"Batch operations may modify multiple records in a single transaction.",severity:"info"}),i}var Oe=[{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}}],ha=K.object({entityLogicalName:K.string().min(1).regex(/^[a-z_][a-z0-9_]*$/,"Invalid logical name"),confirm:K.literal(!0,{errorMap:()=>({message:"Set confirm: true to modify entity metadata"})}),hasNotes:K.boolean().optional(),changeTrackingEnabled:K.boolean().optional(),isAuditEnabled:K.boolean().optional(),autoPublish:K.boolean().optional().default(!1)}).refine(n=>n.hasNotes!==void 0||n.changeTrackingEnabled!==void 0||n.isAuditEnabled!==void 0,{message:"At least one of hasNotes, changeTrackingEnabled, or isAuditEnabled must be provided"});async function Le(n,i,a){switch(n){case"dataverse_update_entity":{let{entityLogicalName:e,hasNotes:t,changeTrackingEnabled:r,isAuditEnabled:s,autoPublish:l}=ha.parse(i),o=$({toolName:"dataverse_update_entity",entitySetName:e}).map(f=>`[${f.severity.toUpperCase()}] ${f.code}: ${f.message}`),c={};t!==void 0&&(c.HasNotes=t),r!==void 0&&(c.ChangeTrackingEnabled=r),s!==void 0&&(c.IsAuditEnabled=s);try{await a.getTableMetadata(e,!1)}catch{return L({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),s!==void 0&&(d.IsAuditEnabled={Value:s});try{await a.updateEntityDefinition(e,d)}catch(f){if((f instanceof Error?f.message:String(f)).includes("0x80060888")){let Q=Object.keys(c),G={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"},Z=Q.map(z=>G[z]).filter(z=>z!==void 0);return L({summary:`Cannot update entity '${e}': operation not supported by Dataverse.`,data:{error:"0x80060888",entityLogicalName:e,requestedChanges:c},errorCategory:"SCHEMA_MISMATCH",suggestions:Z})}throw f}let p=Object.entries(c).map(([f,A])=>`${f}=${String(A)}`).join(", "),m="autoPublish=false (skipped)";return l&&(await a.publishCustomizations({entities:[e]}),m="published successfully"),u(`Entity '${e}' updated: ${p}. ${m}.`,{entityLogicalName:e,changes:c,published:l,...o.length>0&&{warnings:o}},["Use dataverse_get_table_metadata to verify the changes","Enable HasNotes=true before using dataverse_create_annotation"])}default:throw new Error(`Unknown metadata tool: ${n}`)}}var va={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}},ba=Ue.object({name:Ue.string().min(1)}),Pe=[...ue,va,...Oe];async function Me(n,i,a){if(n==="dataverse_resolve_entity_name"){let{name:t}=ba.parse(i),r=`LogicalName eq '${t}' or EntitySetName eq '${t}'`,l=(await a.query("EntityDefinitions",{filter:r,select:["LogicalName","EntitySetName","DisplayName","PrimaryIdAttribute","PrimaryNameAttribute","OwnershipType","IsCustomEntity"]})).value??[];if(l.length===0)return u(`No entity found matching '${t}'`,{name:t,matches:[]},["Try dataverse_list_tables to see available table names"]);let o=l[0],c=o.DisplayName?.UserLocalizedLabel?.Label??null;return u(`Resolved '${t}' \u2192 LogicalName: ${o.LogicalName}, EntitySetName: ${o.EntitySetName}`,{logicalName:o.LogicalName,entitySetName:o.EntitySetName,displayName:c,primaryIdAttribute:o.PrimaryIdAttribute,primaryNameAttribute:o.PrimaryNameAttribute,ownershipType:o.OwnershipType,isCustomEntity:o.IsCustomEntity},["Use entitySetName in OData Web API URLs (dataverse_query, dataverse_read_record)","Use logicalName in FetchXML and metadata API calls"])}return new Set(ue.map(t=>t.name)).has(n)?xe(n,i,a):Le(n,i,a)}import{z as w}from"zod";import{z as qe}from"zod";var $e=/^[a-zA-Z_][a-zA-Z0-9_]*$/,b=qe.string().min(1).regex($e,"entitySetName must contain only letters, digits, or underscores"),re=qe.string().min(1).regex($e,"relationshipName must contain only letters, digits, or underscores");var _a={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"},He=[{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. Always specify $select to minimize payload. For complex aggregations (count, sum, avg), multi-entity joins, many-to-many traversal, or advanced FetchXML-only operators, use dataverse_execute_fetchxml instead. WHEN TO USE: Single-table reads, simple filters, shallow expands, or server-side aggregation via $apply. BEST PRACTICES: Always pass $select; cap with $top; use $apply for server-side counts/grouping. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'The OData entity set name (e.g., "accounts", "contacts", "new_mytables")'},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: 50)"},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:"When true, includes human-readable labels for picklist fields alongside raw integer codes (e.g., { value: 1, label: 'Active' }). Uses OData formatted-value annotations."}},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. 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."}},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. 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."}},required:["entitySetName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],wa=w.object({entitySetName:b,select:w.array(w.string()).optional(),filter:w.string().optional(),orderby:w.string().optional(),top:w.number().positive().max(5e3).optional().default(50),expand:w.string().optional(),count:w.boolean().optional(),apply:w.string().optional(),formattedValues:w.boolean().optional()}),Na=w.object({fetchXml:w.string().min(1).describe("Complete FetchXML query string"),entitySetName:w.string().optional().describe('OData entity set name (e.g., "accounts"). If omitted, extracted from the <entity name="..."> element in the FetchXML.'),fetchAll:w.boolean().optional(),formattedValues:w.boolean().optional()}),Sa=w.object({entitySetName:b,select:w.array(w.string()).optional(),filter:w.string().optional(),orderby:w.string().optional(),expand:w.string().optional(),maxTotal:w.number().positive().max(5e4).optional(),formattedValues:w.boolean().optional()});async function We(n,i,a,e){switch(n){case"dataverse_query":{let t=wa.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.top!==void 0&&(r.top=t.top),t.expand!==void 0&&(r.expand=t.expand),t.count!==void 0&&(r.count=t.count),t.apply!==void 0&&(r.apply=t.apply),t.formattedValues!==void 0&&(r.formattedValues=t.formattedValues);let s;try{s=await a.query(t.entitySetName,r)}catch(f){let A=f,G=A?.data?.error?.message,Z=f instanceof Error?f.message:String(f),z=A?.status,ca=z===403||z===401?"PERMISSIONS":z===404?"SCHEMA_MISMATCH":"UNKNOWN";return ne(`dataverse_query failed on '${t.entitySetName}': ${G??Z}`,ca,["Use dataverse_list_tables to verify the entity set name","Check OData filter syntax and column names"])}let l=Array.isArray(s?.value)?s.value:[],o=s["@odata.count"],c=o!==void 0?o===l.length?" (showing all results)":` (total in dataset: ${o})`:"",d=t.formattedValues?V(l):l,p=t.formattedValues&&Array.isArray(s?.value)?{...s,value:d}:s,m=pe({...t.top!==void 0?{top:t.top}:{},...t.select!==void 0?{select:t.select}:{},...t.filter!==void 0?{filter:t.filter}:{},entitySetName:t.entitySetName}).map(f=>`[${f.severity.toUpperCase()}] ${f.code}: ${f.message}`);return u(`${l.length} records returned from ${t.entitySetName}${c}`,p,["Use dataverse_execute_fetchxml for complex joins or aggregations","Add $select to minimize payload"],m.length>0?m:void 0)}case"dataverse_execute_fetchxml":{let t=Na.parse(i),r=t.entitySetName,{fetchXml:s}=t;if(!r){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 p=d[1];r=_a[p]??p+"s"}if(t.fetchAll){let d=await a.executeFetchXmlAllPages(r,s),p=t.formattedValues?V(d):d;return u(`${p.length} records retrieved via FetchXML (all pages)`,p,["Use dataverse_query for simple OData reads","fetchAll paginated through all available pages (up to 100)"])}let l=await a.executeFetchXml(r,s,t.formattedValues),o=Array.isArray(l)?l:Array.isArray(l?.value)?l.value:[],c=t.formattedValues?V(o):o;return u(`${c.length} records returned via FetchXML`,t.formattedValues&&Array.isArray(l?.value)?{...l,value:c}:l,["Use dataverse_query for simple OData reads","Add page/count attributes for large result sets"])}case"dataverse_retrieve_multiple_with_paging":{let t=Sa.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 s=await a.queryWithPaging(t.entitySetName,r),l=s?.totalRetrieved??(Array.isArray(s?.value)?s.value.length:0),o=s?.pageCount??1;await e?.report(1,1);let c=Array.isArray(s?.records)?s.records:[],d=t.formattedValues&&c.length>0?{...s,records:V(c)}:s,p=pe({...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 u(`${l} records retrieved across ${o} pages from ${t.entitySetName}`,d,["Set maxTotal to limit retrieval","Use $select to minimize payload size"],p.length>0?p:void 0)}default:throw new Error(`Unknown query tool: ${n}`)}}import{z as y}from"zod";var je=[{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."}},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}}],Ta=y.object({entitySetName:b,id:y.string().uuid(),select:y.array(y.string()).optional(),expand:y.string().optional(),formattedValues:y.boolean().optional()}),Ra=y.object({entitySetName:b,data:y.record(y.unknown())}),Aa=y.object({entitySetName:b,id:y.string().uuid(),data:y.record(y.unknown()),etag:y.string().optional()}),ka=y.object({entitySetName:b,id:y.string().uuid(),confirm:y.boolean()}),Ea=y.object({entitySetName:b,alternateKey:y.string().min(1).optional(),alternateKeyValue:y.string().min(1).optional(),alternateKeys:y.record(y.string()).optional(),data:y.record(y.unknown()),mode:y.enum(["upsert","createOnly","updateOnly"]).default("upsert").optional().describe("upsert=create or update (default), createOnly=fail if exists, updateOnly=fail if not found")}).refine(n=>n.alternateKey&&n.alternateKeyValue||n.alternateKeys&&Object.keys(n.alternateKeys).length>0,{message:"Provide either (alternateKey + alternateKeyValue) or alternateKeys"}),Ca=y.object({entitySetName:b,id:y.string().uuid(),ownerType:y.enum(["systemuser","team"]),ownerId:y.string().uuid()});async function Fe(n,i,a){switch(n){case"dataverse_get":{let{entitySetName:e,id:t,select:r,expand:s,formattedValues:l}=Ta.parse(i),o,c;try{let p=await a.getRecord(e,t,r,s);o=p.record,c=p.etag}catch(p){let m=p,A=m?.data?.error?.message,Q=p instanceof Error?p.message:String(p),G=m?.status,Z=G===403||G===401?"PERMISSIONS":G===404?"SCHEMA_MISMATCH":"UNKNOWN";return ne(`dataverse_get failed for '${t}' in '${e}': ${A??Q}`,Z,["Use dataverse_list_tables to verify the entity set name","Verify the record ID exists with dataverse_query"])}let d=l?V([o])[0]??o:o;return u(`Retrieved record ${t} from ${e}`,{id:t,record:d,etag:c},["Use dataverse_update to modify this record","Use dataverse_get_relationships to explore related records"])}case"dataverse_create":{let{entitySetName:e,data:t}=Ra.parse(i),r=await a.createRecord(e,t);return u(`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:s}=Aa.parse(i);return await a.updateRecord(e,t,r,s),u(`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}=ka.parse(i);return r?(await a.deleteRecord(e,t),u(`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=Ea.parse(i),{entitySetName:t,alternateKey:r,alternateKeyValue:s,alternateKeys:l,data:o,mode:c="upsert"}=e,d;l&&Object.keys(l).length>0&&(d=Object.entries(l).map(([m,f])=>`${v(m)}='${v(f)}'`).join(","));let p=await a.upsertRecord(t,r??"",s??"",o,c,d);return u(`Upsert ${p.operation}: record in ${t}`,{operation:p.operation,id:p.id,message:`Record ${p.operation} successfully`},["Use dataverse_get_entity_key to verify alternate key definitions"])}case"dataverse_assign":{let{entitySetName:e,id:t,ownerType:r,ownerId:s}=Ca.parse(i),l=r==="systemuser"?"systemusers":"teams";return await a.updateRecord(e,t,{"ownerid@odata.bind":`/${l}(${s})`}),u(`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: ${n}`)}}import{z as T}from"zod";var Be=[{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. WHEN TO USE: Linking two records via an N:N relationship or 1:N navigation property. BEST PRACTICES: Get the relationship schema name from dataverse_get_relationships first; for simple 1:N lookups use dataverse_update. WORKFLOW: update_record.",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. WHEN TO USE: Bulk N:N association setup (e.g., assigning many privileges to a role, linking many contacts to a campaign). BEST PRACTICES: Check existing associations first with dataverse_query; runs concurrently so order is not guaranteed. WORKFLOW: update_record.",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. WHEN TO USE: Removing an N:N or 1:N link between two records without deleting either record. BEST PRACTICES: Get the relationship schema name from dataverse_get_relationships; relatedId is required for N:N. WORKFLOW: update_record.",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. WHEN TO USE: Check if a role already has specific privileges before adding them; check campaign members; check group participants. BEST PRACTICES: Use $select to return only needed columns; set $top to limit results. Example: to check role privileges, query entitySetName='roles', navigationProperty='roleprivileges_association' with $select='name,privilegeid'. WORKFLOW: read_record, inspect_audit.",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"}},required:["entitySetName","id","navigationProperty"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Da=T.object({entitySetName:b,id:T.string().uuid(),relationshipName:re,relatedEntitySetName:b,relatedIds:T.array(T.string().uuid()).min(1).max(200)}),Ia=T.object({entitySetName:b,id:T.string().uuid(),relationshipName:re,relatedEntitySetName:b,relatedId:T.string().uuid()}),xa=T.object({entitySetName:b,id:T.string().uuid(),relationshipName:re,relatedId:T.string().uuid().optional(),relatedEntitySetName:b.optional(),confirm:T.literal(!0)}),Oa=/^[A-Za-z_][A-Za-z0-9_]*$/,La=T.object({entitySetName:b,id:T.string().uuid(),navigationProperty:T.string().min(1).regex(Oa,"Invalid navigation property name"),select:T.string().optional(),top:T.number().int().min(1).max(1e3).optional(),filter:T.string().optional()});async function Ge(n,i,a){switch(n){case"dataverse_associate_bulk":{let{entitySetName:e,id:t,relationshipName:r,relatedEntitySetName:s,relatedIds:l}=Da.parse(i),o=await Promise.allSettled(l.map(p=>a.associate(e,t,r,s,p))),c=[],d=[];return o.forEach((p,m)=>{p.status==="fulfilled"?c.push(l[m]):d.push({id:l[m],error:p.reason instanceof Error?p.reason.message:String(p.reason)})}),u(`${c.length}/${l.length} associations succeeded`,{entitySetName:e,id:t,relationshipName:r,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:t,relationshipName:r,relatedEntitySetName:s,relatedId:l}=Ia.parse(i);return await a.associate(e,t,r,s,l),u(`Associated ${e}(${t}) with ${s}(${l}) 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:s,relatedEntitySetName:l}=xa.parse(i);return await a.disassociate(e,t,r,s,l),u(`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:s,top:l,filter:o}=La.parse(i),c=`${e}(${t})/${r}`,p=(await a.query(c,{...s?{select:s.split(",").map(m=>m.trim())}:{},top:l??50,...o?{filter:o}:{}})).value??[];return u(`${p.length} associated records via ${r}`,{entitySetName:e,id:t,navigationProperty:r,count:p.length,records:p},["Use dataverse_associate to add new associations","Use dataverse_disassociate to remove existing ones"])}default:throw new Error(`Unknown relation tool: ${n}`)}}import{z as _}from"zod";var ze=[{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. WHEN TO USE: Invoking global Dataverse actions or custom process actions like WinOpportunity or SendEmail. BEST PRACTICES: Check action parameters via metadata or docs; use dataverse_execute_bound_action for record-scoped actions. WORKFLOW: update_record.",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. WHEN TO USE: Calling global read-only OData functions like RetrieveTotalRecordCount or InitializeFrom. BEST PRACTICES: Functions are side-effect-free and safe to retry; use dataverse_execute_action for mutations. WORKFLOW: query_data.",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. WHEN TO USE: Running an action scoped to a specific record, e.g. QualifyLead on a lead. BEST PRACTICES: Omit the Microsoft.Dynamics.CRM namespace prefix; ensure the record exists first. WORKFLOW: update_record.",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. WHEN TO USE: Before deleting a solution component to check for blocking dependencies. BEST PRACTICES: Resolve all dependencies before attempting deletion. WORKFLOW: manage_solution.",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. WHEN TO USE: Calling a read-only computed function on a specific record, e.g. CalculateRollupField. BEST PRACTICES: Functions are side-effect-free and safe to call repeatedly; pass parameters as string key-value pairs. WORKFLOW: query_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. WHEN TO USE: Before modifying or removing a table, to discover workflows, flows, and plugins that reference it. BEST PRACTICES: Filter by componentType to focus on specific dependency kinds. WORKFLOW: manage_solution.",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}}],ie=/^[a-zA-Z0-9_.]+$/,Ua=_.object({actionName:_.string().min(1).regex(ie,"actionName must contain only letters, digits, underscores, or dots"),parameters:_.record(_.unknown()).optional().default({})}),Pa=_.object({functionName:_.string().min(1).regex(ie,"functionName must contain only letters, digits, underscores, or dots"),parameters:_.record(_.unknown()).optional().default({})}),Ma=_.object({entitySetName:b,id:_.string().uuid(),actionName:_.string().min(1).regex(ie,"actionName must contain only letters, digits, underscores, or dots"),parameters:_.record(_.unknown()).optional().default({})}),qa=_.object({componentType:_.number().int().positive(),objectId:_.string().uuid()}),$a=["Workflow","Flow","BusinessRule","Action","BusinessProcessFlow","Plugin","CustomAPI"],Ha=_.object({tableName:_.string().min(1),componentType:_.array(_.enum($a)).optional()}),Wa=_.object({entitySetName:b,id:_.string().uuid(),functionName:_.string().min(1).regex(ie,"functionName must contain only letters, digits, underscores, or dots"),parameters:_.record(_.unknown()).optional().default({})});async function Ve(n,i,a){switch(n){case"dataverse_execute_action":{let{actionName:e,parameters:t}=Ua.parse(i),r=await a.executeAction(e,t);return u(`Executed unbound action ${e}`,r,["Use dataverse_list_custom_actions to discover available actions"])}case"dataverse_execute_function":{let{functionName:e,parameters:t}=Pa.parse(i),r=await a.executeFunction(e,t);return u(`Executed function ${e}`,r,["Functions are read-only operations"])}case"dataverse_execute_bound_action":{let{entitySetName:e,id:t,actionName:r,parameters:s}=Ma.parse(i),l=await a.executeBoundAction(e,t,r,s);return u(`Executed bound action ${r} on ${e}(${t})`,l)}case"dataverse_list_dependencies":{let{tableName:e,componentType:t}=Ha.parse(i),r=await a.listTableDependencies(e,t);return u(`${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}=qa.parse(i),r=await a.listDependencies(e,t),s=Array.isArray(r)?r:r.value??[r];return u(`${s.length} dependencies found`,r,["Review dependencies before deleting the component"])}case"dataverse_execute_bound_function":{let{entitySetName:e,id:t,functionName:r,parameters:s}=Wa.parse(i),l=await a.executeBoundFunction(e,t,r,s);return u(`Executed bound function ${r} on ${e}(${t})`,l)}default:throw new Error(`Unknown action tool: ${n}`)}}import{z as W}from"zod";var Ke=[{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}}],ja=W.object({method:W.enum(["GET","POST","PATCH","DELETE"]),url:W.string().min(1).refine(n=>!/[\r\n]/.test(n),{message:"Batch URL must not contain CR or LF characters"}).refine(n=>!n.startsWith("http"),{message:"Batch URL must be a relative path, not an absolute URL"}).refine(n=>!/(\.\.[\/\\])|(^\.\.$)/.test(n),{message:"Batch URL must not contain path traversal sequences"}),body:W.record(W.unknown()).optional()}),Fa=W.object({requests:W.array(ja).min(1).max(1e3),useChangeset:W.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 Xe(n,i,a,e){if(n==="dataverse_batch_execute"){let{requests:t,useChangeset:r}=Fa.parse(i),s=t.map(c=>({method:c.method,url:c.url,body:c.body}));await e?.report(0,t.length);let l=await a.batchExecute(s,r);await e?.report(t.length,t.length);let o=l.filter(c=>c===null||!c.error).length;return u(`Batch executed: ${o}/${l.length} operations succeeded`,{results:l,count:l.length},["Use batch for bulk create/update operations to reduce HTTP round-trips"])}throw new Error(`Unknown batch tool: ${n}`)}import{z as se}from"zod";var Qe=[{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)"}},required:["entitySetName","deltaToken"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Ba=se.object({entitySetName:b,deltaToken:se.string().nullable(),select:se.array(se.string()).optional()});async function Ye(n,i,a){if(n==="dataverse_change_detection"){let{entitySetName:e,deltaToken:t,select:r}=Ba.parse(i),s;try{s=await a.getChangedRecords(e,t,r)}catch(o){let c=o instanceof Error?o.message:String(o);if(/change.?track|0x80072491/i.test(c))return Y({type:"feature_disabled",feature:"Change Tracking",cannotProceedBecause:`Change tracking is not enabled on '${e}', 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 '${e}'`,"Open the table \u2192 click the Settings (gear) icon",'Enable "Track changes"',"Save the table, then publish customizations"],fixableViaToolName:"dataverse_update_entity"});throw o}let l=Array.isArray(s?.newAndModified)?s.newAndModified:[];return u(`${l.length} changed records detected`,s,["Store the returned deltaToken for subsequent incremental sync"])}throw new Error(`Unknown tracking tool: ${n}`)}import{z as j}from"zod";var Ga=j.object({components:j.object({entities:j.array(j.string()).optional().describe("Entity logical names to publish"),webResources:j.array(j.string()).optional().describe("Web resource names to publish"),optionSets:j.array(j.string()).optional().describe("Global OptionSet names to publish")}).optional().describe("Specific components to publish. If omitted, ALL unpublished customizations are published.")}),Je=[{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 Ze(n,i,a){switch(n){case"dataverse_publish_customizations":{let{components:e}=Ga.parse(i??{}),t=await a.publishCustomizations(e);return u("Customizations published successfully",t,["Changes are now visible to all users in the environment"])}default:throw new Error(`Unknown solution tool: ${n}`)}}import{z as te}from"zod";var za=te.object({callerId:te.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:te.string().min(1).describe('Name of the MCP tool to execute on behalf of the user (e.g., "dataverse_create", "dataverse_query")'),toolArgs:te.record(te.unknown()).describe("Arguments for the wrapped tool, as an object")}),me=[{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 et(n,i,a,e){if(n!=="dataverse_impersonate")throw new Error(`Unknown impersonate tool: ${n}`);let{callerId:t,toolName:r,toolArgs:s}=za.parse(i);try{if(((await a.query(`systemusers(${v(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(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 l=a.http,o=l.defaultHeaders.MSCRMCallerId;try{l.defaultHeaders.MSCRMCallerId=t;let c=await e(r,s,a);return u(`Impersonated as ${t}, executed ${r}`,{impersonatedAs:t,tool:r,result:JSON.parse(c.content[0].text)},["Impersonation applies to this single call only"])}finally{o===void 0?delete l.defaultHeaders.MSCRMCallerId:l.defaultHeaders.MSCRMCallerId=o}}import{z as E}from"zod";var Va={10:"Pre-validation",20:"Pre-operation",40:"Post-operation",45:"Post-operation (deprecated)"},Ka={0:"Synchronous",1:"Asynchronous"},Xa=E.object({workflowId:E.string().uuid(),activate:E.boolean()}),tt=[{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}}],Qa=E.object({top:E.number().positive().max(500).optional().default(100),nameFilter:E.string().optional()}),Ya=E.object({top:E.number().positive().max(500).optional().default(100),activeOnly:E.boolean().optional().default(!0),entityLogicalName:E.string().optional()}),Ja=E.object({connectorId:E.string().optional(),activeOnly:E.boolean().optional().default(!1),top:E.number().int().positive().max(500).optional().default(100)});async function at(n,i,a){switch(n){case"dataverse_list_custom_actions":{let{top:e,nameFilter:t}=Qa.parse(i??{}),r=["isprivate eq false"];t&&r.push(`contains(name,'${v(t)}')`);let l=(await a.query("sdkmessages",{select:["sdkmessageid","name","categoryname","isprivate","isreadonly","isvalidforexecuteasync"],filter:r.join(" and "),top:e})).value.map(o=>({id:o.sdkmessageid,name:o.name,category:o.categoryname??"",isPrivate:o.isprivate,isReadOnly:o.isreadonly,asyncSupported:o.isvalidforexecuteasync}));return u(`${l.length} custom actions found`,{total:l.length,messages:l},["Use dataverse_execute_action to run an action"])}case"dataverse_list_plugin_steps":{let{top:e,activeOnly:t,entityLogicalName:r}=Ya.parse(i??{}),s={select:["sdkmessageprocessingstepid","name","stage","mode","rank","statecode","filteringattributes","asyncautodelete"],expand:"sdkmessageid($select=name,categoryname),plugintypeid($select=name,assemblyname),sdkmessagefilterid($select=primaryobjecttypecode)",top:e};t&&(s.filter="statecode eq 0");let o=(await a.query("sdkmessageprocessingsteps",s)).value;if(r){let d=r.toLowerCase();o=o.filter(p=>p.sdkmessagefilterid?.primaryobjecttypecode?.toLowerCase()===d)}let c=o.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:Va[d.stage]??`Stage ${d.stage}`,mode:d.mode,modeName:Ka[d.mode]??`Mode ${d.mode}`,rank:d.rank,isActive:d.statecode===0,filteringAttributes:d.filteringattributes??null,asyncAutoDelete:d.asyncautodelete}));return u(`${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:t}=Xa.parse(i);try{await a.updateRecord("workflows",e,{statecode:t?1:0,statuscode:t?2:1})}catch(r){let s=r instanceof Error?r.message:String(r);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}`):r}return u(`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}=Ja.parse(i??{}),s=[];t&&s.push("statecode eq 0"),e&&s.push(`contains(connectorid,'${v(e)}')`);let l={select:["connectionreferenceid","connectionreferencelogicalname","connectionreferencedisplayname","connectorid","connectionid","statecode","statuscode"],orderby:"connectionreferencedisplayname asc",top:r};s.length&&(l.filter=s.join(" and "));let c=((await a.query("connectionreferences",l)).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,p=d>0?`${c.length} connection references found (${d} inactive \u2014 check before deploying)`:`${c.length} connection references found`;return u(p,{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: ${n}`)}}import{z as D}from"zod";var Za={1e8:"String",100000001:"Number",100000002:"Boolean",100000003:"JSON",100000004:"DataSource"},en={String:1e8,Integer:100000001,Boolean:100000002,JSON:100000003},nt=[{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}}],tn=D.object({schemaName:D.string().min(1)}),an=D.object({schemaName:D.string().min(1),value:D.string()}),nn=D.object({schemaName:D.string().min(1),displayName:D.string().min(1),type:D.enum(["String","Integer","Boolean","JSON"]),value:D.string(),description:D.string().optional(),defaultValue:D.string().optional(),confirm:D.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a new environment variable definition"})})});async function rn(n,i){let{schemaName:a}=tn.parse(n),t=(await i.query("environmentvariabledefinitions",{filter:`schemaname eq '${v(a)}'`,select:["environmentvariabledefinitionid","schemaname","displayname","description","type","defaultvalue","isrequired"],top:1})).value;if(t.length===0)throw new Error(`Environment variable '${a}' 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],s=r.environmentvariabledefinitionid,o=(await i.query("environmentvariablevalues",{filter:`_environmentvariabledefinitionid_value eq ${s}`,select:["environmentvariablevalueid","value"],top:1})).value,c=o.length>0?o[0]:null,d=r.type,p=c?c.value??null:null,m=r.defaultvalue??null,f={schemaName:r.schemaname,displayName:r.displayname??"",description:r.description??"",type:d,typeName:Za[d]??"Unknown",defaultValue:m,currentValue:p,valueId:c?c.environmentvariablevalueid:null,isRequired:r.isrequired??!1,effectiveValue:p??m};return u(`Environment variable '${f.schemaName}': ${f.typeName} = ${f.effectiveValue??"(not set)"}`,f,["Use dataverse_set_environment_variable to update the value"])}async function sn(n,i){let{schemaName:a,value:e}=an.parse(n),r=(await i.query("environmentvariabledefinitions",{filter:`schemaname eq '${v(a)}'`,select:["environmentvariabledefinitionid","schemaname"],top:1})).value;if(r.length===0)throw new Error(`Environment variable definition '${a}' 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=r[0].environmentvariabledefinitionid,o=(await i.query("environmentvariablevalues",{filter:`_environmentvariabledefinitionid_value eq ${s}`,select:["environmentvariablevalueid","value"],top:1})).value,c,d;o.length>0?(d=o[0].environmentvariablevalueid,await i.updateRecord("environmentvariablevalues",d,{value:e}),c="updated"):(d=await i.createRecord("environmentvariablevalues",{value:e,"EnvironmentVariableDefinitionId@odata.bind":`/environmentvariabledefinitions(${s})`}),c="created");let p={schemaName:a,operation:c,valueId:d,value:e};return u(`Environment variable '${a}' set to new value`,p,["Use dataverse_get_environment_variable to verify the update"])}async function on(n,i){let{schemaName:a,displayName:e,type:t,value:r,description:s,defaultValue:l}=nn.parse(n),o=$({toolName:"dataverse_create_environment_variable"}).map(A=>`[${A.severity.toUpperCase()}] ${A.code}: ${A.message}`);if((await i.query("environmentvariabledefinitions",{filter:`schemaname eq '${v(a)}'`,select:["environmentvariabledefinitionid"],top:1})).value.length>0)throw new Error(`Environment variable '${a}' already exists. Use dataverse_set_environment_variable to update its value.`);let d=en[t]??1e8,p={schemaname:a,displayname:e,type:d};s&&(p.description=s),l!==void 0&&(p.defaultvalue=l);let m=await i.createRecord("environmentvariabledefinitions",p),f=await i.createRecord("environmentvariablevalues",{value:r,"EnvironmentVariableDefinitionId@odata.bind":`/environmentvariabledefinitions(${m})`});return u(`Environment variable '${a}' created (type: ${t}, value: '${r}').`,{schemaName:a,displayName:e,type:t,typeCode:d,definitionId:m,valueId:f,value:r,...o.length>0&&{warnings:o}},["Use dataverse_get_environment_variable to verify","Use dataverse_set_environment_variable to update the value later"])}async function rt(n,i,a){switch(n){case"dataverse_get_environment_variable":return rn(i,a);case"dataverse_set_environment_variable":return sn(i,a);case"dataverse_create_environment_variable":return on(i,a);case"dataverse_environment_capabilities":{let[e,t,r,s]=await Promise.allSettled([a.executeFunction("WhoAmI",{}),a.query("organizations",{select:["uniquename","friendlyname","version","languagecode","isauditenabled"],top:1}),a.query("solutions",{filter:"ismanaged eq false",select:["uniquename"]}),a.query("environmentvariabledefinitions",{select:["schemaname"],top:1})]),l=e.status==="fulfilled"?e.value:null,o=t.status==="fulfilled"?t.value.value:[],c=r.status==="fulfilled"?r.value.value:[],d=s.status==="fulfilled"?s.value.value:[];return u("Environment capabilities retrieved",{identity:l,organization:o[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: ${n}`)}}import{z as M}from"zod";var ln={0:"Execute",1:"Create",2:"Retrieve",3:"RetrieveMultiple",4:"GetParent",5:"Update",6:"Delete",7:"Assign"},cn={0:"Waiting",10:"WaitingForResources",20:"InProgress",21:"Pausing",22:"Canceling",30:"Succeeded",31:"Failed",32:"Canceled"},dn={0:"ReadyToRun",1:"Suspended",2:"Locked",3:"Completed"},un=["plugintracelogid","typename","messagename","primaryentity","depth","operationtype","exceptiondetails","messageblock","createdon","performanceexecutionduration","correlationid","requestid"],pn=["asyncoperationid","name","operationtype","statuscode","statecode","message","createdon","startedon","completedon"],it=[{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. 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)"},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. 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)"},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}}],mn=M.object({top:M.number().int().positive().max(200).optional().default(50),pluginTypeFilter:M.string().optional(),messageFilter:M.string().optional(),entityFilter:M.string().optional(),exceptionsOnly:M.boolean().optional().default(!1)}),gn=M.object({top:M.number().int().positive().max(200).optional().default(50),failedOnly:M.boolean().optional().default(!1),entityFilter:M.string().optional()});function P(n){return typeof n=="string"?n:""}function fn(n){return typeof n=="number"?n:null}async function st(n,i,a){switch(n){case"dataverse_get_plugin_trace_logs":{let e=mn.parse(i),t=[];e.pluginTypeFilter&&t.push(`contains(typename,'${v(e.pluginTypeFilter)}')`),e.messageFilter&&t.push(`messagename eq '${v(e.messageFilter)}'`),e.entityFilter&&t.push(`primaryentity eq '${v(e.entityFilter)}'`),e.exceptionsOnly&&t.push("exceptiondetails ne null");let r=t.length>0?t.join(" and "):void 0,o=((await a.query("plugintracelogs",{select:un,orderby:"createdon desc",top:e.top,...r!==void 0?{filter:r}:{}})).value??[]).map(d=>{let p=typeof d.operationtype=="number"?d.operationtype:0,m=d.exceptiondetails;return{id:P(d.plugintracelogid),typeName:P(d.typename),message:P(d.messagename),entity:P(d.primaryentity),depth:typeof d.depth=="number"?d.depth:0,operationType:p,operationTypeName:ln[p]??String(p),createdOn:P(d.createdon),durationMs:fn(d.performanceexecutionduration),correlationId:P(d.correlationid),requestId:P(d.requestid),hasException:m!=null&&m!=="",exceptionDetails:typeof m=="string"?m:null,messageBlock:typeof d.messageblock=="string"?d.messageblock:null}}),c={total:o.length,logs:o};return u(`${o.length} plugin trace logs found`,c,["Filter by plugin name or correlation ID for specific traces"])}case"dataverse_get_workflow_trace_logs":{let e=gn.parse(i),t=["operationtype eq 10"];e.failedOnly&&t.push("statuscode eq 31");let r=t.join(" and "),o=((await a.query("asyncoperations",{select:pn,filter:r,orderby:"createdon desc",top:e.top})).value??[]).map(d=>{let p=typeof d.statuscode=="number"?d.statuscode:0,m=typeof d.statecode=="number"?d.statecode:0,f=d.message;return{id:P(d.asyncoperationid),name:P(d.name),statusCode:p,statusName:cn[p]??String(p),stateCode:m,stateName:dn[m]??String(m),createdOn:P(d.createdon),startedOn:typeof d.startedon=="string"?d.startedon:null,completedOn:typeof d.completedon=="string"?d.completedon:null,errorMessage:typeof f=="string"?f:null}}),c={total:o.length,workflows:o};return u(`${o.length} workflow trace logs found`,c,["Filter by status or entity type for specific workflow traces"])}default:throw new Error(`Unknown trace tool: ${n}`)}}import{z as C}from"zod";var ot=[{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."}},required:["query"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],yn=C.object({query:C.string().min(1),entities:C.array(C.string()).optional(),top:C.number().int().positive().max(50).optional().default(10),searchMode:C.enum(["any","all"]).optional().default("any"),searchType:C.enum(["simple","full"]).optional(),filter:C.string().optional(),facets:C.array(C.string()).optional(),orderby:C.array(C.string()).optional(),select:C.array(C.string()).optional()});async function lt(n,i,a){switch(n){case"dataverse_search":{let e=yn.parse(i),t={search:e.query,top:e.top};if(e.entities?.length){let o=e.entities.map(c=>({name:c,...e.select?.length?{selectColumns:e.select}:{}}));t.entities=JSON.stringify(o)}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 a.search(t)}catch(o){let c=o instanceof Error?o.message:String(o);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 Y({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 o}let s=(r.value??[]).map(o=>({entityName:o.entityname??"",objectId:o.objectid??"",score:o.score??0,highlights:o.highlights??{},fields:o.attributes??{}})),l={totalRecordCount:r.totalrecordcount??0,results:s};return r.facets&&(l.facets=r.facets),u(`${s.length} search results for '${e.query}'`,l,["Use dataverse_get to retrieve full record details","Narrow results with entities[] filter"])}default:throw new Error(`Unknown search tool: ${n}`)}}import{z as F}from"zod";var ge={1:"Create",2:"Update",3:"Delete",4:"Activate",5:"Deactivate",11:"Share",12:"Unshare",13:"Assign",104:"Access"},hn=Object.fromEntries(Object.entries(ge).map(([n,i])=>[i,Number(n)])),ct=[{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"'}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],vn=F.object({recordId:F.string().uuid().optional(),entityLogicalName:F.string().min(1).optional(),userId:F.string().uuid().optional(),fromDate:F.string().datetime({offset:!0}).optional(),top:F.number().positive().max(500).optional().default(50),operations:F.array(F.string().min(1)).optional()});function bn(n){if(!n)return{};try{return JSON.parse(n)}catch{return n}}function _n(n){return{auditId:n.auditid,operation:n.operation,operationName:ge[n.operation]??`Unknown(${n.operation})`,action:n.action,actionName:ge[n.action]??`Unknown(${n.action})`,createdOn:n.createdon,userId:n._userid_value,userFullName:n.userid?.fullname??"",userDomainName:n.userid?.domainname??"",objectId:n._objectid_value,objectTypeCode:n.objecttypecode,changes:bn(n.changedata)}}async function dt(n,i,a){switch(n){case"dataverse_get_audit_log":{let e=vn.parse(i),t=[];if(e.recordId&&t.push(`_objectid_value eq ${e.recordId}`),e.entityLogicalName){let r=v(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(s=>hn[s]).filter(s=>s!==void 0);if(r.length>0){let s=r.map(l=>`action eq ${l}`).join(" or ");t.push(`(${s})`)}}try{let s=(await a.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(_n);return u(`${s.length} audit records for ${e.entityLogicalName??e.recordId??"query"}`,{entries:s,count:s.length},["Filter by operation type for specific changes"])}catch(r){let s=r instanceof Error?r.message:String(r);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 r}}default:throw new Error(`Unknown audit tool: ${n}`)}}import{z as J}from"zod";var ut=new Map,pt=[{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}}],wn=J.object({entityLogicalName:J.string().min(1).regex(/^[a-z_][a-z0-9_]*$/i,"Must be a valid Dataverse logical name"),record:J.record(J.string(),J.unknown()),top:J.number().int().positive().max(20).optional().default(5)});async function mt(n,i,a){switch(n){case"dataverse_detect_duplicates":{let r=function(m){return String(m).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")};var e=r;let t=wn.parse(i),s=Object.entries(t.record).filter(([,m])=>m!=null).map(([m,f])=>`<condition attribute="${r(m)}" operator="eq" value="${r(String(f))}" />`).join(`
|
|
3
|
-
`);if(!
|
|
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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")};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
4
|
<entity name="${r(t.entityLogicalName)}">
|
|
5
5
|
<all-attributes />
|
|
6
6
|
<filter type="or">
|
|
7
|
-
${
|
|
7
|
+
${o}
|
|
8
8
|
</filter>
|
|
9
9
|
</entity>
|
|
10
|
-
</fetch>`,c=await a.executeFetchXml(l,o),p=(Array.isArray(c.value)?c.value:[]).map(m=>{let f={};for(let[A,Q]of Object.entries(m))A.startsWith("@")||(f[A]=Q);return f});return u(`Duplicate detection: ${p.length>0?`${p.length} potential duplicate(s) found`:"no duplicates found"}`,{hasDuplicates:p.length>0,duplicateCount:p.length,duplicates:p,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: ${n}`)}}import{z as x}from"zod";var Nn=x.object({recordId:x.string().uuid(),includeContent:x.boolean().optional().default(!1),top:x.number().int().positive().max(100).optional().default(20),mimeTypeFilter:x.string().optional()}),Sn=x.object({recordId:x.string().uuid(),entitySetName:b,noteText:x.string().optional(),subject:x.string().optional(),fileName:x.string().optional(),mimeType:x.string().optional(),documentBody:x.string().optional()}).strict().refine(n=>!!n.noteText||!!n.documentBody,{message:"At least one of noteText or documentBody is required"});function Tn(n){return{opportunities:"opportunity",territories:"territory",categories:"category",queues:"queue",activities:"activitypointer"}[n]??n.replace(/s$/,"")}var gt=[{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")'}},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 ft(n,i,a){switch(n){case"dataverse_get_annotations":{let e=Nn.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 '${v(e.mimeTypeFilter)}'`);let o=((await a.query("annotations",{select:t,filter:r.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 u(`${o.length} annotations found for record ${e.recordId}`,{recordId:e.recordId,annotations:o,count:o.length},["Use dataverse_create_annotation to add a note or attachment"])}case"dataverse_create_annotation":{let e=Sn.parse(i),t=Tn(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 s;try{s=await a.createRecord("annotations",r)}catch(l){let o=String(l);if(o.includes("0x80048d19")||o.includes("objectid_")&&o.includes("undeclared"))return Y({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 l}return u(`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: ${n}`)}}import{z as S}from"zod";var Rn=S.object({userId:S.string().uuid()}),An=S.object({search:S.string().optional(),businessUnitId:S.string().optional(),includeDisabled:S.boolean().optional().default(!1),includeApplicationUsers:S.boolean().optional().default(!1),top:S.number().int().positive().max(100).optional().default(20)}),kn=S.object({nameContains:S.string().optional(),businessUnitId:S.string().optional(),top:S.number().int().positive().max(200).optional().default(50)}),En=S.object({userId:S.string().uuid(),roleId:S.string().uuid(),confirm:S.literal(!0,{errorMap:()=>({message:"confirm must be true to assign a role"})})}),Cn=S.object({userId:S.string().uuid(),roleId:S.string().uuid(),confirm:S.literal(!0,{errorMap:()=>({message:"confirm must be true to remove a role"})})}),yt=[{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"}},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)"}}},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)"}},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 ht(n,i,a){switch(n){case"dataverse_get_user_roles":{let{userId:e}=Rn.parse(i),r=(await a.query("systemusers",{filter:`systemuserid eq ${e}`,select:["fullname","domainname"],expand:"systemuserroles_association($select=name,roleid,ismanaged)",top:1})).value??[];if(r.length===0)throw new Error(`User with ID '${e}' not found`);let s=r[0],o=(s.systemuserroles_association??[]).map(c=>({roleId:c.roleid??"",name:c.name??"",isManaged:c.ismanaged===!0}));return u(`${o.length} roles assigned to user`,{userId:e,fullname:s.fullname??"",domainname:s.domainname??"",roles:o,roleCount:o.length},["Use dataverse_impersonate to execute operations as this user"])}case"dataverse_list_users":{let e=An.parse(i),t=[];if(e.includeDisabled||t.push("isdisabled eq false"),e.includeApplicationUsers||t.push("applicationid eq null"),e.search){let o=v(e.search);t.push(`(contains(fullname,'${o}') or contains(internalemailaddress,'${o}'))`)}e.businessUnitId&&t.push(`_businessunitid_value eq ${e.businessUnitId}`);let l=((await a.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(o=>({id:o.systemuserid??"",fullName:o.fullname??"",domainName:o.domainname??"",email:o.internalemailaddress??"",businessUnit:o.businessunitid?.name??null,isDisabled:o.isdisabled===!0,isApplicationUser:o.applicationid!=null}));return u(`${l.length} users found`,{users:l,count:l.length},["Use dataverse_get_user_roles to inspect specific user permissions"])}case"dataverse_list_roles":{let{nameContains:e,businessUnitId:t,top:r}=kn.parse(i),s=[];e&&s.push(`contains(name,'${v(e)}')`),t&&s.push(`_businessunitid_value eq ${t}`);let l=s.length?s.join(" and "):void 0,c=((await a.query("roles",{select:["roleid","name","description","ismanaged","_businessunitid_value"],...l!==void 0?{filter:l}:{},orderby:"name asc",top:r})).value??[]).map(d=>({id:d.roleid??"",name:d.name??"",businessUnitId:d._businessunitid_value??null,isManaged:d.ismanaged===!0,description:d.description??""}));return u(`${c.length} security roles found`,{roles:c,count:c.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}=En.parse(i);return(((await a.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?u("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 a.associate("systemusers",e,"systemuserroles_association","roles",t),u("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}=Cn.parse(i);return(((await a.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?u("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 a.disassociate("systemusers",e,"systemuserroles_association",t,"roles"),u("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: ${n}`)}}import{z as I}from"zod";var fe={ADD_PRIVILEGES_ROLE:"AddPrivilegesRole",REPLACE_PRIVILEGES_ROLE:"ReplacePrivilegesRole"},vt={RETRIEVE_ROLE_PRIVILEGES:"RetrieveRolePrivilegesRole",WHO_AM_I:"WhoAmI"};var Dn=I.enum(["None","Basic","Local","Deep","Global"]),In=I.object({roleId:I.string().uuid()}),_t=I.object({privilegeId:I.string().uuid(),depth:Dn,businessUnitId:I.string().uuid().optional()}),xn=I.object({roleId:I.string().uuid(),privileges:I.array(_t).min(1)}),On=I.object({roleId:I.string().uuid(),privileges:I.array(_t).min(1),confirm:I.literal(!0,{errorMap:()=>({message:"confirm must be true to replace all privileges on the role"})})}),bt={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"]}},wt=[{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.
|
|
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.
|
|
11
11
|
|
|
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:bt},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:bt,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 Nt(n,i,a){switch(n){case"dataverse_get_role_privileges":{let{roleId:e}=In.parse(i),r=(await a.executeFunction(vt.RETRIEVE_ROLE_PRIVILEGES,{RoleId:e}))?.RolePrivileges??[];return u(`${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=xn.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 a.executeBoundAction("roles",e.roleId,fe.ADD_PRIVILEGES_ROLE,{Privileges:t}),u(`${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=On.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 a.executeBoundAction("roles",e.roleId,fe.REPLACE_PRIVILEGES_ROLE,{Privileges:t}),u(`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: ${n}`)}}import{z as oe}from"zod";var Ln=oe.object({entityLogicalName:oe.string().min(1),includePersonal:oe.boolean().optional().default(!1),top:oe.number().int().positive().max(100).optional().default(20)}),St=[{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)"}},required:["entityLogicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function Tt(n,i,a){switch(n){case"dataverse_list_views":{let{entityLogicalName:e,includePersonal:t,top:r}=Ln.parse(i),s=v(e),o=(await a.query("savedqueries",{filter:`returnedtypecode eq '${s}' and statecode eq 0`,select:["savedqueryid","name","isdefault","querytype","description"],orderby:"name asc",top:r})).value.map(p=>({id:p.savedqueryid,name:p.name,isDefault:p.isdefault,queryType:p.querytype,description:p.description??null,viewType:"system"})),c=[];t&&(c=(await a.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"})));let d=o.length+c.length;return u(`${d} views found for ${e}`,{entityLogicalName:e,systemViews:o,systemViewCount:o.length,personalViews:t?c:void 0,personalViewCount:t?c.length:void 0},["Use the view's fetchxml with dataverse_execute_fetchxml to run it"])}default:throw new Error(`Unknown view tool: ${n}`)}}import{z as ye}from"zod";var Un=ye.object({top:ye.number().int().positive().max(200).optional().default(50),includeDisabled:ye.boolean().optional().default(!1)}),Rt=[{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)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function At(n,i,a){switch(n){case"dataverse_list_business_units":{let{top:e,includeDisabled:t}=Un.parse(i??{}),r=t?void 0:"isdisabled eq false",l=(await a.query("businessunits",{select:["businessunitid","name","parentbusinessunitid","isdisabled","createdon"],...r?{filter:r}:{},orderby:"name asc",top:e})).value.map(o=>({id:o.businessunitid,name:o.name,parentBusinessUnitId:o.parentbusinessunitid??null,isDisabled:o.isdisabled,createdOn:o.createdon}));return u(`${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: ${n}`)}}import{z as q}from"zod";var kt=/^[a-zA-Z0-9_]+$/,Et=/\.\.|[/\\]/,Pn=q.object({entitySetName:q.string().min(1).refine(n=>!Et.test(n),{message:"entitySetName must not contain path traversal characters"}),recordId:q.string().uuid(),columnName:q.string().min(1).refine(n=>kt.test(n),{message:"columnName must be alphanumeric/underscore only"}),fileContent:q.string().min(1).describe("Base64-encoded file content"),fileName:q.string().min(1)}),Mn=q.object({entitySetName:q.string().min(1).refine(n=>!Et.test(n),{message:"entitySetName must not contain path traversal characters"}),recordId:q.string().uuid(),columnName:q.string().min(1).refine(n=>kt.test(n),{message:"columnName must be alphanumeric/underscore only"})}),Ct=[{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 Dt(n,i,a){let e=a.http;switch(n){case"dataverse_upload_file_column":{let{entitySetName:t,recordId:r,columnName:s,fileContent:l,fileName:o}=Pn.parse(i),c=Buffer.from(l,"base64"),d=`${t}(${r})/${s}`;return await e.patch(d,c,{headers:{"Content-Type":"application/octet-stream","x-ms-file-name":o}}),u(`Uploaded file to ${s} on ${t}(${r})`,{success:!0,entitySetName:t,recordId:r,columnName:s,fileName:o,sizeBytes:c.length},["Use dataverse_download_file_column to retrieve the file"])}case"dataverse_download_file_column":{let{entitySetName:t,recordId:r,columnName:s}=Mn.parse(i),l=`${t}(${r})/${s}/$value`,o=await e.get(l,{responseType:"text"}),c=o.data,d=Buffer.from(c,"binary").toString("base64"),p=o.headers["x-ms-file-name"]??o.headers["content-disposition"]?.match(/filename="?([^";\n]+)"?/)?.[1]??"download",m=parseInt(o.headers["content-length"]??"0",10)||d.length;return u(`Downloaded file from ${s} on ${t}(${r})`,{entitySetName:t,recordId:r,columnName:s,fileName:p,sizeBytes:m,contentBase64:d})}default:throw new Error(`Unknown file tool: ${n}`)}}import{z as U}from"zod";var qn={0:"Owner",1:"Access",2:"Office",3:"Security"},$n=U.object({top:U.number().int().positive().max(200).optional().default(50),teamType:U.union([U.literal(0),U.literal(1),U.literal(2),U.literal(3)]).optional()}),Hn=U.object({teamId:U.string().uuid(),roleId:U.string().uuid(),confirm:U.literal(!0,{errorMap:()=>({message:"confirm must be true to assign a role to a team"})})}),It=[{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"}},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 xt(n,i,a){switch(n){case"dataverse_list_teams":{let{top:e,teamType:t}=$n.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 o=(await a.query("teams",s)).value.map(c=>({...c,teamTypeName:qn[c.teamtype]??"Unknown"}));return u(`${o.length} teams found`,{teams:o,count:o.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}=Hn.parse(i);return(((await a.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?u("Role is already assigned to this team.",{teamId:e,roleId:t,status:"already_assigned"},["Use dataverse_list_teams to view current team configuration"]):(await a.associate("teams",e,"teamroles_association","roles",t),u("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: ${n}`)}}var ae={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"]},he={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 Wn(n){return n.toLowerCase().replace(/[^a-z0-9_]/g," ").split(/\s+/).filter(i=>i.length>1)}function jn(n,i){let a=ae[n]??[],e=(he[n]??"").toLowerCase(),t=n.toLowerCase(),r=0;for(let s of i)a.includes(s)&&(r+=3),t.includes(s)&&(r+=2),e.includes(s)&&(r+=1);return r}function Fn(n){let i=Wn(n);if(i.length===0)return["dataverse_whoami","dataverse_list_tables","dataverse_query","dataverse_search","dataverse_get_table_metadata"].map(t=>({name:t,description:he[t]??"",tags:ae[t]??[],score:1}));let a=[];for(let e of Object.keys(ae)){let t=jn(e,i);t>0&&a.push({name:e,description:he[e]??"",tags:ae[e]??[],score:t})}return a.sort((e,t)=>t.score-e.score),a.slice(0,5)}function Bn(){let n=new Map;for(let i of Object.values(ae))for(let a of i)n.set(a,(n.get(a)??0)+1);return Array.from(n.entries()).map(([i,a])=>({tag:i,toolCount:a})).sort((i,a)=>a.toolCount-i.toolCount)}var Ot=[{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 Lt(n,i,a){if(n==="dataverse_suggest_tools"){let{intent:e}=i??{},t=Fn(e??""),r=t.length>0?[`Try: ${t[0].name}`]:["Use dataverse_list_tool_tags to explore categories"];return u(`${t.length} tools match intent "${e??""}"`,t,r)}if(n==="dataverse_list_tool_tags"){let e=Bn();return H("tags",e,["Use dataverse_suggest_tools with an intent to find specific tools"])}throw new Error(`Unknown router tool: ${n}`)}var Gn=[{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"}],zn=[{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 Vn(n){let i="dataverse://";if(!n.startsWith(i))throw new Error(`Unknown resource URI: ${n}`);let a=n.slice(i.length);if(a==="tables")return{type:"tables"};if(a==="server/instructions")return{type:"instructions"};let e=/^tables\/([^/]+)\/schema$/.exec(a);if(e)return{type:"schema",tableName:e[1]};let t=/^tables\/([^/]+)\/relationships$/.exec(a);if(t)return{type:"relationships",tableName:t[1]};throw new Error(`Unknown resource URI: ${n}`)}function Ut(){return zn}function Pt(){return Gn}async function Mt(n,i,a){let e=Vn(n);switch(e.type){case"tables":{let t=await i.listTables(!0);return{uri:n,mimeType:"application/json",text:JSON.stringify(t,null,2)}}case"schema":{let t=await i.getTableMetadata(e.tableName);return{uri:n,mimeType:"application/json",text:JSON.stringify(t,null,2)}}case"relationships":{let t=await i.getRelationships(e.tableName);return{uri:n,mimeType:"application/json",text:JSON.stringify(t,null,2)}}case"instructions":return{uri:n,mimeType:"text/plain",text:a}}}var Kn=[{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 qt(){return Kn}function $t(n,i={}){switch(n){case"analyze-org-health":return Xn();case"data-quality-check":return Qn(i);case"schema-review":return Yn(i);case"security-audit":return Jn();case"analyze-workflow":return Zn(i);default:throw new Error(`Unknown prompt: ${n}`)}}function Xn(){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.
|
|
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.
|
|
13
13
|
|
|
14
14
|
**Step 1 \u2014 Identity**
|
|
15
15
|
Call \`dataverse_whoami\` to confirm the connected user, org ID, and environment URL.
|
|
@@ -35,13 +35,13 @@ Produce a structured report with these sections:
|
|
|
35
35
|
- \u2705 Healthy items
|
|
36
36
|
- \u26A0\uFE0F Warnings (non-blocking but should be addressed)
|
|
37
37
|
- \u{1F534} Issues (blocking or high-risk)
|
|
38
|
-
- \u{1F4A1} Top 5 recommended actions, prioritised by impact`}}]}}function
|
|
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.
|
|
39
39
|
|
|
40
40
|
**Step 1 \u2014 Schema baseline**
|
|
41
41
|
Call \`dataverse_get_table_metadata\` for \`${i}\`. Identify: required vs optional columns, column types (text/lookup/optionset/datetime), and any deprecated column types.
|
|
42
42
|
|
|
43
43
|
**Step 2 \u2014 Sample data**
|
|
44
|
-
Call \`dataverse_query\` on \`${i}\` with \`$top=${
|
|
44
|
+
Call \`dataverse_query\` on \`${i}\` with \`$top=${n}\` and \`$orderby=modifiedon desc\`. Select all non-binary columns.
|
|
45
45
|
|
|
46
46
|
**Step 3 \u2014 Null analysis**
|
|
47
47
|
For each optional column in the sample: calculate the null/empty rate. Flag columns where > 30% of sampled records have no value.
|
|
@@ -59,7 +59,7 @@ For each Lookup column in the schema: spot-check 5 records to verify the referen
|
|
|
59
59
|
Produce:
|
|
60
60
|
- \u{1F4CA} Overall quality score (0\u2013100)
|
|
61
61
|
- Issues grouped by: Completeness | Uniqueness | Freshness | Integrity
|
|
62
|
-
- Top 5 action items with effort estimate (Low / Medium / High)`}}]}}function
|
|
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}**.
|
|
63
63
|
|
|
64
64
|
**Step 1 \u2014 Table metadata**
|
|
65
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.
|
|
@@ -91,7 +91,7 @@ Produce:
|
|
|
91
91
|
- Schema Maturity Score (0\u2013100)
|
|
92
92
|
- Critical issues (must fix before going live)
|
|
93
93
|
- Recommendations (best-practice improvements)
|
|
94
|
-
- Quick wins (< 1 hour, low risk)`}}]}}function
|
|
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.
|
|
95
95
|
|
|
96
96
|
**Step 1 \u2014 Users**
|
|
97
97
|
Call \`dataverse_list_users\` (top: 200). Identify:
|
|
@@ -118,10 +118,10 @@ Produce:
|
|
|
118
118
|
- \u{1F534} Critical issues (immediate action required)
|
|
119
119
|
- \u{1F7E1} Moderate findings (plan remediation)
|
|
120
120
|
- \u2705 Good practices observed
|
|
121
|
-
- Priority action list (top 5, sorted by risk)`}}]}}function
|
|
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}**.
|
|
122
122
|
|
|
123
123
|
**Step 1 \u2014 Inventory**
|
|
124
|
-
Call \`dataverse_list_workflows\` with status: "${
|
|
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.
|
|
125
125
|
|
|
126
126
|
**Step 2 \u2014 Ownership check**
|
|
127
127
|
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 +145,39 @@ Identify and report:
|
|
|
145
145
|
- Duplicate workflows performing near-identical logic
|
|
146
146
|
- Workflows that run synchronously and could be made asynchronous to improve user responsiveness
|
|
147
147
|
|
|
148
|
-
Produce: Workflow Health Matrix | Issues by severity | Migration candidates prioritised by effort`}}]}}var le=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 Wt=[{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)."}},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"}},required:["workflowId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}}],Ht={0:"Classic Workflow",1:"Dialog",2:"Business Rule",3:"Action",4:"Business Process Flow",6:"Power Automate (Cloud Flow)"};async function jt(n,i,a){if(n==="dataverse_list_guides"){let e=Array.from(le.values()).map(t=>({name:t.name,description:t.description,tags:t.tags,stepCount:t.steps.length}));return H("guides",e,["Use dataverse_get_guide with a guide name to see detailed steps"])}if(n==="dataverse_get_guide"){let{name:e}=i,t=le.get(e);if(!t){let r=Array.from(le.keys()).join(", ");throw new Error(`Unknown guide: "${e}". Available guides: ${r}`)}return u(`Guide "${t.name}": ${t.description}`,t,t.steps.map(r=>`Step ${r.order}: ${r.tool} \u2014 ${r.description}`))}if(n==="dataverse_list_workflows"){let e=i,r=(await a.listDataverseWorkflows({...e.category!==void 0?{category:e.category}:{},...e.nameContains!==void 0?{nameContains:e.nameContains}:{},top:Math.min(e.top??50,200)})).map(s=>({workflowId:s.workflowid,name:s.name,category:Ht[s.category]??s.category,state:s.statecode===1?"Active":"Inactive",modifiedOn:s.modifiedon}));return H(`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(n==="dataverse_get_workflow"){let{workflowId:e}=i,t=await a.getDataverseWorkflow(e),r=t.category,s={...t,categoryLabel:Ht[r]??`Category ${r}`,stateLabel:t.statecode===1?"Active":"Inactive"};return u(`Workflow: ${t.name}`,s,[])}throw new Error(`Unknown workflow tool: ${n}`)}import{z as g}from"zod";var X=/^[a-z_][a-z0-9_]*$/,ve=["String","Memo","Integer","Decimal","Money","DateTime","Boolean","Picklist","MultiSelectPicklist","AutoNumber","Image"],er={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"},Ft=g.object({entityLogicalName:g.string().min(1).regex(X,"Invalid Dataverse logical name"),schemaName:g.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/,"Invalid schema name"),attributeType:g.enum(ve),displayName:g.string().min(1),description:g.string().optional(),requiredLevel:g.enum(["None","ApplicationRequired","Recommended"]).optional().default("None"),maxLength:g.number().int().positive().optional(),minValue:g.number().optional(),maxValue:g.number().optional(),precision:g.number().int().min(0).max(10).optional(),dateTimeFormat:g.enum(["DateOnly","DateAndTime"]).optional().default("DateAndTime"),defaultBooleanValue:g.boolean().optional().default(!1),picklistOptions:g.array(g.object({value:g.number().int(),label:g.string().min(1)})).optional(),autoNumberFormat:g.string().optional(),languageCode:g.number().int().optional().default(1033),autoPublish:g.boolean().optional().default(!1),confirm:g.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a column"})})}).refine(n=>n.attributeType!=="Picklist"&&n.attributeType!=="MultiSelectPicklist"||n.picklistOptions&&n.picklistOptions.length>0,{message:"picklistOptions is required for Picklist and MultiSelectPicklist types and must have at least one option"}),Bt=g.object({entityLogicalName:g.string().min(1).regex(X,"Invalid Dataverse logical name"),attributeLogicalName:g.string().min(1).regex(X,"Invalid Dataverse logical name"),displayName:g.string().min(1).optional(),description:g.string().optional(),requiredLevel:g.enum(["None","ApplicationRequired","Recommended"]).optional(),maxLength:g.number().int().positive().optional(),isSearchable:g.boolean().optional(),languageCode:g.number().int().optional().default(1033),autoPublish:g.boolean().optional().default(!1),confirm:g.literal(!0,{errorMap:()=>({message:"Set confirm: true to update column metadata"})})}).refine(n=>n.displayName!==void 0||n.description!==void 0||n.requiredLevel!==void 0||n.maxLength!==void 0||n.isSearchable!==void 0,{message:"At least one mutable property (displayName, description, requiredLevel, maxLength, isSearchable) must be provided"}),Gt=g.object({entityLogicalName:g.string().min(1).regex(X,"Invalid Dataverse logical name"),attributeLogicalName:g.string().min(1).regex(X,"Invalid Dataverse logical name"),autoPublish:g.boolean().optional().default(!1),confirm:g.literal(!0,{errorMap:()=>({message:"Set confirm: true to delete a column \u2014 this is irreversible"})})});function O(n,i){return{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:n,LanguageCode:i}]}}function zt(n){let i=n.languageCode,a={"@odata.type":er[n.attributeType],SchemaName:n.schemaName,DisplayName:O(n.displayName,i),RequiredLevel:{Value:n.requiredLevel}};switch(n.description&&(a.Description=O(n.description,i)),n.attributeType){case"String":a.MaxLength=n.maxLength??100;break;case"Memo":a.MaxLength=n.maxLength??4e3;break;case"Integer":a.Format="None",n.minValue!==void 0&&(a.MinValue=n.minValue),n.maxValue!==void 0&&(a.MaxValue=n.maxValue);break;case"Decimal":n.precision!==void 0&&(a.Precision=n.precision),n.minValue!==void 0&&(a.MinValue=n.minValue),n.maxValue!==void 0&&(a.MaxValue=n.maxValue);break;case"Money":a.PrecisionSource=n.precision!==void 0?0:2,n.precision!==void 0&&(a.Precision=n.precision);break;case"DateTime":a.Format=n.dateTimeFormat;break;case"Boolean":a.DefaultValue=n.defaultBooleanValue,a.OptionSet={TrueOption:{Value:1,Label:O("Yes",i)},FalseOption:{Value:0,Label:O("No",i)}};break;case"Picklist":a.OptionSet={"@odata.type":"Microsoft.Dynamics.CRM.OptionSetMetadata",IsGlobal:!1,OptionSetType:"Picklist",Options:n.picklistOptions.map(e=>({Value:e.value,Label:O(e.label,i)}))};break;case"MultiSelectPicklist":a.OptionSet={"@odata.type":"Microsoft.Dynamics.CRM.OptionSetMetadata",IsGlobal:!1,Options:n.picklistOptions.map(e=>({Value:e.value,Label:O(e.label,i)}))};break;case"AutoNumber":a.MaxLength=n.maxLength??100,n.autoNumberFormat&&(a.AutoNumberFormat=n.autoNumberFormat);break;case"Image":break}return a}var Vt=g.object({entityLogicalName:g.string().min(1).regex(X),schemaName:g.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/),displayName:g.string().min(1),description:g.string().optional(),referencedEntity:g.string().min(1).regex(X),requiredLevel:g.enum(["None","ApplicationRequired","Recommended"]).optional().default("None"),cascadeConfiguration:g.enum(["Referential","Parental"]).optional().default("Referential"),languageCode:g.number().int().optional().default(1033),autoPublish:g.boolean().optional().default(!1),confirm:g.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a lookup column"})})});function Kt(n){let i=n.languageCode,a=n.schemaName.includes("_")?n.schemaName.slice(0,n.schemaName.indexOf("_")):n.schemaName,e=n.schemaName.includes("_")?n.schemaName.slice(n.schemaName.indexOf("_")+1):n.schemaName,t=`${a}_${n.referencedEntity}_${n.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:n.schemaName,DisplayName:O(n.displayName,i),RequiredLevel:{Value:n.requiredLevel}};return n.description&&(r.Description=O(n.description,i)),{"@odata.type":"Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",SchemaName:t,ReferencedEntity:n.referencedEntity,ReferencedAttribute:`${n.referencedEntity}id`,ReferencingEntity:n.entityLogicalName,Lookup:r,CascadeConfiguration:n.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:O(n.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:ve,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 Xt(n,i,a){switch(n){case"dataverse_create_attribute":{let e=Ft.parse(i),t=$({toolName:"dataverse_create_attribute",entitySetName:e.entityLogicalName}).map(o=>`[${o.severity.toUpperCase()}] ${o.code}: ${o.message}`),r=zt(e),s=await a.createAttribute(e.entityLogicalName,r),l="autoPublish=false (skipped)";return e.autoPublish&&(await a.publishCustomizations({entities:[e.entityLogicalName]}),l="published successfully"),u(`Column '${e.schemaName}' (${e.attributeType}) created on '${e.entityLogicalName}'. ${l}.`,{entityLogicalName:e.entityLogicalName,schemaName:e.schemaName,attributeType:e.attributeType,metadataId:s,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=Bt.parse(i),t=$({toolName:"dataverse_update_attribute",entitySetName:e.entityLogicalName}).map(d=>`[${d.severity.toUpperCase()}] ${d.code}: ${d.message}`),r=e.languageCode,s={},l={};e.displayName!==void 0&&(s.DisplayName=O(e.displayName,r),l.DisplayName=e.displayName),e.description!==void 0&&(s.Description=O(e.description,r),l.Description=e.description),e.requiredLevel!==void 0&&(s.RequiredLevel={Value:e.requiredLevel},l.RequiredLevel=e.requiredLevel),e.maxLength!==void 0&&(s.MaxLength=e.maxLength,l.MaxLength=e.maxLength),e.isSearchable!==void 0&&(s.IsSearchable=e.isSearchable,l.IsSearchable=e.isSearchable),await a.updateAttribute(e.entityLogicalName,e.attributeLogicalName,s);let o=Object.entries(l).map(([d,p])=>`${d}=${String(p)}`).join(", "),c="autoPublish=false (skipped)";return e.autoPublish&&(await a.publishCustomizations({entities:[e.entityLogicalName]}),c="published successfully"),u(`Column '${e.attributeLogicalName}' on '${e.entityLogicalName}' updated: ${o}. ${c}.`,{entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,changes:l,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=Gt.parse(i),t=$({toolName:"dataverse_delete_attribute",entitySetName:e.entityLogicalName}).map(s=>`[${s.severity.toUpperCase()}] ${s.code}: ${s.message}`);try{await a.deleteAttribute(e.entityLogicalName,e.attributeLogicalName)}catch(s){let l=s instanceof Error?s.message:String(s);if(l.includes("0x80048405"))return L({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(l.includes("dependency")||l.includes("dependencies")||l.includes("DependencyList"))return L({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:l},errorCategory:"SCHEMA_MISMATCH",suggestions:["Use dataverse_list_dependencies to identify what references this column","Remove all references before retrying deletion"]});let o=l.match(/0x[0-9a-fA-F]{8}/),c=o?o[0]:"UNKNOWN";return L({summary:`Cannot delete column '${e.attributeLogicalName}': deletion rejected by Dataverse (${c}).`,data:{error:c,entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,rawError:l},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 a.publishCustomizations({entities:[e.entityLogicalName]}),r="published successfully"),u(`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=Vt.parse(i),t=$({toolName:"dataverse_create_lookup_attribute",entitySetName:e.entityLogicalName}).map(c=>`[${c.severity.toUpperCase()}] ${c.code}: ${c.message}`),r=Kt(e),s,l=!1;try{s=await a.createRelationship(r)}catch(c){if((c instanceof Error?c.message:String(c)).includes("Request timed out")){let p=!1;try{p=((await a.getTableMetadata(e.entityLogicalName,!0)).Attributes??[]).some(A=>A.LogicalName===e.schemaName.toLowerCase())}catch{}if(p)l=!0,s="";else throw c}else throw c}let o="autoPublish=false (skipped)";return e.autoPublish&&!l?(await a.publishCustomizations({entities:[e.entityLogicalName,e.referencedEntity]}),o="published successfully"):l&&(o="publish skipped (request timed out \u2014 use dataverse_publish_customizations to publish)"),l&&t.push("REQUEST TIMED OUT but lookup was found on the table \u2014 creation succeeded. MetadataId unavailable."),u(`Lookup column '${e.schemaName}' created on '${e.entityLogicalName}' \u2192 '${e.referencedEntity}'. ${o}.`,{entityLogicalName:e.entityLogicalName,schemaName:e.schemaName,referencedEntity:e.referencedEntity,...s&&{metadataId:s},published:e.autoPublish&&!l,...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: ${n}`)}}import{z as R}from"zod";function B(n){return n.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function tr(n){let i=0,a=0,e=0;return`<SiteMap>
|
|
149
|
-
${
|
|
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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}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)}">
|
|
150
150
|
`+d.join(`
|
|
151
151
|
`)+`
|
|
152
|
-
</Group>`});return` <Area Id="${
|
|
153
|
-
`+
|
|
152
|
+
</Group>`});return` <Area Id="${le(o)}" Title="${le(r.title)}" ShowGroups="true">
|
|
153
|
+
`+s.join(`
|
|
154
154
|
`)+`
|
|
155
155
|
</Area>`}).join(`
|
|
156
156
|
`)}
|
|
157
|
-
</SiteMap>`}var ar=R.object({id:R.string().optional(),entityLogicalName:R.string().optional(),url:R.string().optional(),title:R.string().optional()}),nr=R.object({id:R.string().optional(),title:R.string(),subareas:R.array(ar).min(1)}),rr=R.object({id:R.string().optional(),title:R.string(),groups:R.array(nr).min(1)}),ir=R.object({sitemapName:R.string().min(1),appModuleUniqueName:R.string().optional(),areas:R.array(rr).min(1)}),Qt=[{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 Yt(n,i,a){switch(n){case"dataverse_create_sitemap":{let{sitemapName:e,appModuleUniqueName:t,areas:r}=ir.parse(i),s=tr(r),l=await a.createRecord("sitemaps",{sitemapname:e,sitemapxml:s}),o=!1,c=null;if(t){let d=await a.query("appmodules",{select:["appmoduleid"],filter:`uniquename eq '${t}'`,top:1});if(d.value.length===0)return u(`Sitemap '${e}' created (id: ${l}) \u2014 app module not found`,{sitemapId:l,sitemapName:e,linked:!1,warning:`App module '${t}' 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 a.updateRecord("appmodules",c,{"sitemapid_sitemap@odata.bind":`/sitemaps(${l})`}),o=!0}return u(`Sitemap '${e}' created successfully`,{sitemapId:l,sitemapName:e,linked:o,appModuleUniqueName:t??null,appModuleId:c,areaCount:r.length,sitemapxml:s},["Run dataverse_publish_customizations to publish the sitemap",o?`Sitemap linked to app '${t}' (appmoduleid: ${c})`:"Provide appModuleUniqueName to link this sitemap to a model-driven app"])}default:throw new Error(`Unknown sitemap tool: ${n}`)}}import{z as N}from"zod";var _e=[{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'."}},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 sr=N.object({schemaName:N.string().min(1).describe("Schema name of the new table (e.g., 'new_order'). Must include a publisher prefix."),displayName:N.string().min(1).describe("Singular display name (e.g., 'Order')."),displayCollectionName:N.string().min(1).describe("Plural display name (e.g., 'Orders')."),description:N.string().optional().describe("Optional description of the table's purpose."),ownershipType:N.enum(["UserOwned","OrganizationOwned"]).optional().default("UserOwned").describe("Ownership model. 'UserOwned' supports record-level sharing."),hasNotes:N.boolean().optional().default(!1).describe("Allow notes/annotations on this table."),hasActivities:N.boolean().optional().default(!1).describe("Allow activities (email, task, appointment) on this table."),isAuditEnabled:N.boolean().optional().default(!1).describe("Enable audit logging for this table."),primaryNameColumnSchemaName:N.string().optional().describe("Schema name for the primary name column. Defaults to '{schemaName}_name'."),primaryNameColumnDisplayName:N.string().optional().default("Name").describe("Display name for the primary name column. Default: 'Name'.")}),or=N.object({type:N.enum(["OneToMany","ManyToMany"]).describe("Relationship type."),schemaName:N.string().min(1).describe("Schema name for the relationship (e.g., 'new_account_order')."),referencedEntity:N.string().optional().describe("(OneToMany) Logical name of the 'one' side entity (e.g., 'account')."),referencingEntity:N.string().optional().describe("(OneToMany) Logical name of the 'many' side entity (e.g., 'new_order')."),referencingAttributeSchemaName:N.string().optional().describe("(OneToMany) Schema name of the lookup column on the referencing entity."),referencingAttributeDisplayName:N.string().optional().describe("(OneToMany) Display name for the lookup column."),entity1LogicalName:N.string().optional().describe("(ManyToMany) First entity logical name."),entity2LogicalName:N.string().optional().describe("(ManyToMany) Second entity logical name."),intersectEntitySchemaName:N.string().optional().describe("(ManyToMany) Schema name for the auto-generated intersect table."),cascadeDelete:N.enum(["Cascade","RemoveLink","Restrict","NoCascade"]).optional().default("RemoveLink").describe("(OneToMany) Delete cascade behavior on the referencing side. Default: RemoveLink.")});async function Jt(n,i,a){switch(n){case"dataverse_create_table":return lr(i,a);case"dataverse_create_relationship":return cr(i,a);default:throw new Error(`Unknown schema tool: ${n}`)}}async function lr(n,i){let a=sr.parse(n),e=a.primaryNameColumnSchemaName??`${a.schemaName.toLowerCase()}_name`,t={"@odata.type":"Microsoft.Dynamics.CRM.EntityMetadata",SchemaName:a.schemaName,DisplayName:{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:a.displayName,LanguageCode:1033}]},DisplayCollectionName:{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:a.displayCollectionName,LanguageCode:1033}]},OwnershipType:a.ownershipType,HasNotes:a.hasNotes,HasActivities:a.hasActivities,IsAuditEnabled:{Value:a.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:a.primaryNameColumnDisplayName??"Name",LanguageCode:1033}]},MaxLength:100}]};a.description&&(t.Description={"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:a.description,LanguageCode:1033}]});let r,s=!1;try{r=await i.createEntityDefinition(t)}catch(o){let c=o instanceof Error?o.message:String(o);if(c.includes("Request timed out")||c.includes("TRANSIENT_LOCK")){let d=!1;try{let p=await i.getTableMetadata(a.schemaName.toLowerCase(),!1);p?.MetadataId&&(r=p.MetadataId,d=!0,s=!0)}catch{}if(!d)throw o}else throw o}let l=s?" (NOTE: the request timed out but the table was confirmed created server-side \u2014 [TRANSIENT_TIMEOUT_RECOVERED])":"";return u(`Table '${a.displayName}' (${a.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.${l}`,{metadataId:r,schemaName:a.schemaName,displayName:a.displayName,displayCollectionName:a.displayCollectionName,ownershipType:a.ownershipType,hasNotes:a.hasNotes,hasActivities:a.hasActivities,isAuditEnabled:a.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 '${a.schemaName.toLowerCase()}'`,`Use dataverse_create_relationship to link '${a.schemaName.toLowerCase()}' to other tables`,`When ALL schema work is done: dataverse_publish_customizations({ components: { entities: ['${a.schemaName.toLowerCase()}', ...otherEntities] } })`])}async function cr(n,i){let a=or.parse(n),e,t;if(a.type==="OneToMany"){if(!a.referencedEntity||!a.referencingEntity)throw new Error("OneToMany relationship requires 'referencedEntity' (the 'one' side) and 'referencingEntity' (the 'many' side).");let l=`${a.referencedEntity}id`,o=(()=>{if(a.referencingAttributeSchemaName)return null;let p=a.referencingEntity.split("_");return p.length>=2?p[0]:null})(),c=a.referencingAttributeSchemaName??(o?`${o}_${a.referencedEntity}id`:`${a.referencedEntity}id`),d=a.referencingAttributeDisplayName??a.referencedEntity.charAt(0).toUpperCase()+a.referencedEntity.slice(1);e={"@odata.type":"Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",SchemaName:a.schemaName,ReferencedEntity:a.referencedEntity,ReferencedAttribute:l,ReferencingEntity:a.referencingEntity,CascadeConfiguration:{"@odata.type":"Microsoft.Dynamics.CRM.CascadeConfiguration",Assign:"NoCascade",Delete:a.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"}}},t=`One-to-Many relationship '${a.schemaName}' created: ${a.referencedEntity} \u2192 ${a.referencingEntity}.`}else{if(!a.entity1LogicalName||!a.entity2LogicalName)throw new Error("ManyToMany relationship requires 'entity1LogicalName' and 'entity2LogicalName'.");let l=a.intersectEntitySchemaName??`${a.entity1LogicalName}_${a.entity2LogicalName}`;e={"@odata.type":"Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata",SchemaName:a.schemaName,Entity1LogicalName:a.entity1LogicalName,Entity2LogicalName:a.entity2LogicalName,IntersectEntityName:l,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 '${a.schemaName}' created: ${a.entity1LogicalName} \u2194 ${a.entity2LogicalName}.`}let r=await i.createRelationship(e),s={metadataId:r,schemaName:a.schemaName,type:a.type};return a.type==="OneToMany"&&(s.cascadeDelete=a.cascadeDelete),u(`${t} MetadataId: ${r}.`,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 h}from"zod";var we=[{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 dr=["ReadAccess","WriteAccess","AppendAccess","AppendToAccess","AssignAccess","ShareAccess","DeleteAccess"],Ne=["systemuser","team"],ur=h.object({entitySetName:h.string().min(1).describe("OData entity set name (plural, e.g., 'accounts')."),recordId:h.string().uuid().describe("GUID of the record to check access on."),principalId:h.string().uuid().describe("GUID of the user or team to check access for."),principalType:h.enum(Ne).optional().default("systemuser").describe("Type of principal. Default: systemuser.")}),pr=h.object({entitySetName:h.string().min(1).describe("OData entity set name for the target record (e.g., 'accounts')."),recordId:h.string().uuid().describe("GUID of the record to share."),principalId:h.string().uuid().describe("GUID of the user or team receiving access."),principalType:h.enum(Ne).optional().default("systemuser").describe("Type of principal. Default: systemuser."),accessRights:h.array(h.enum(dr)).min(1).describe("Rights to grant. Options: ReadAccess, WriteAccess, AppendAccess, AppendToAccess, AssignAccess, ShareAccess, DeleteAccess.")}),mr=h.object({entitySetName:h.string().min(1).describe("OData entity set name for the target record (e.g., 'accounts')."),recordId:h.string().uuid().describe("GUID of the record to revoke access from."),principalId:h.string().uuid().describe("GUID of the user or team losing access."),principalType:h.enum(Ne).optional().default("systemuser").describe("Type of principal. Default: systemuser.")}),gr=h.object({entityLogicalName:h.string().min(1).describe("Logical name of the entity (e.g., 'account', 'contact', 'lead')."),entitySetName:h.string().min(1).describe("OData entity set name (e.g., 'accounts', 'contacts')."),targetId:h.string().uuid().describe("GUID of the master record (kept after merge)."),subordinateId:h.string().uuid().describe("GUID of the subordinate record (deactivated after merge)."),updateContent:h.record(h.unknown()).optional().describe("Fields from the subordinate to copy into the target record."),performParentingChecks:h.boolean().optional().default(!1).describe("Check for parenting conflicts before merge. Default: false."),confirm:h.literal(!0).describe("Must be true to confirm the merge. The subordinate record is deactivated permanently.")});async function ea(n,i,a){switch(n){case"dataverse_check_record_access":return fr(i,a);case"dataverse_grant_access":return yr(i,a);case"dataverse_revoke_access":return hr(i,a);case"dataverse_merge_records":return vr(i,a);default:throw new Error(`Unknown record-access tool: ${n}`)}}function Se(n){return n==="systemuser"?"systemusers":"teams"}function ta(n){return n==="systemuser"?"systemuserid":"teamid"}function aa(n){return n==="systemuser"?"Microsoft.Dynamics.CRM.systemuser":"Microsoft.Dynamics.CRM.team"}async function fr(n,i){let a=ur.parse(n),e=Se(a.principalType),r=(await i.executeBoundFunction(e,a.principalId,"RetrievePrincipalAccess",{Target:{"@odata.id":`${a.entitySetName}(${a.recordId})`}}))?.AccessRights??"None",s=r==="None"?[]:r.split(", ").filter(Boolean);return u(`${a.principalType} ${a.principalId} has [${s.join(", ")||"no access"}] on ${a.entitySetName}(${a.recordId}).`,{accessRights:r,rights:s,principalId:a.principalId,principalType:a.principalType,entitySetName:a.entitySetName,recordId:a.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 yr(n,i){let a=pr.parse(n),e=Se(a.principalType),t=ta(a.principalType),r=aa(a.principalType);return await i.executeAction("GrantAccess",{Target:{"@odata.id":`${a.entitySetName}(${a.recordId})`},PrincipalAccess:{Principal:{"@odata.type":r,[t]:a.principalId,"@odata.id":`${e}(${a.principalId})`},AccessMask:a.accessRights.join(", ")}}),u(`Access granted: ${a.principalType} ${a.principalId} has [${a.accessRights.join(", ")}] on ${a.entitySetName}(${a.recordId}).`,{entitySetName:a.entitySetName,recordId:a.recordId,principalId:a.principalId,principalType:a.principalType,accessRights:a.accessRights},["Use dataverse_check_record_access to verify the granted rights","Use dataverse_revoke_access to remove access if needed"])}async function hr(n,i){let a=mr.parse(n),e=Se(a.principalType),t=ta(a.principalType),r=aa(a.principalType);return await i.executeAction("RevokeAccess",{Target:{"@odata.id":`${a.entitySetName}(${a.recordId})`},Revokee:{"@odata.type":r,[t]:a.principalId,"@odata.id":`${e}(${a.principalId})`}}),u(`Access revoked: ${a.principalType} ${a.principalId} no longer has shared access to ${a.entitySetName}(${a.recordId}).`,{entitySetName:a.entitySetName,recordId:a.recordId,principalId:a.principalId,principalType:a.principalType},["Use dataverse_check_record_access to verify access has been removed"])}var Zt=new Set(["account","contact","lead","incident","opportunity"]);async function vr(n,i){let a=gr.parse(n);if(!Zt.has(a.entityLogicalName))throw new Error(`dataverse_merge_records only supports: ${[...Zt].join(", ")}. '${a.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.${a.entityLogicalName}`,t=`${a.entityLogicalName}id`;return await i.executeAction("Merge",{Target:{"@odata.type":e,[t]:a.targetId,"@odata.id":`${a.entitySetName}(${a.targetId})`},Subordinate:{"@odata.type":e,[t]:a.subordinateId,"@odata.id":`${a.entitySetName}(${a.subordinateId})`},UpdateContent:{"@odata.type":`#Microsoft.Dynamics.CRM.${a.entityLogicalName}`,...a.updateContent??{}},PerformParentingChecks:a.performParentingChecks??!1}),u(`Merge complete: ${a.entityLogicalName}(${a.subordinateId}) has been deactivated and merged into ${a.entityLogicalName}(${a.targetId}).`,{entityLogicalName:a.entityLogicalName,targetId:a.targetId,subordinateId:a.subordinateId},[`Use dataverse_get to verify the merged record: entitySetName='${a.entitySetName}', recordId='${a.targetId}'`,"The subordinate record is now deactivated and cannot be restored"])}var sa=Ee([{tools:Ce,handler:De},{tools:Pe,handler:Me},{tools:He,handler:We},{tools:je,handler:Fe},{tools:Be,handler:Ge},{tools:ze,handler:Ve},{tools:Ke,handler:Xe},{tools:Qe,handler:Ye},{tools:Je,handler:Ze},{tools:tt,handler:at},{tools:nt,handler:rt},{tools:it,handler:st},{tools:ot,handler:lt},{tools:ct,handler:dt},{tools:pt,handler:mt},{tools:gt,handler:ft},{tools:yt,handler:ht},{tools:wt,handler:Nt},{tools:St,handler:Tt},{tools:Rt,handler:At},{tools:Ct,handler:Dt},{tools:It,handler:xt},{tools:Wt,handler:jt},{tools:be,handler:Xt},{tools:Qt,handler:Yt},{tools:_e,handler:Jt},{tools:we,handler:ea},{tools:Ot,handler:Lt}]),xr=new Set(me.map(n=>n.name)),oa=[...sa.getAllDefinitions(),...me],Or=Dr(Ir(import.meta.url)),la=JSON.parse(Er(Cr(Or,"../package.json"),"utf-8")).version,na=`You are connected to a Microsoft Dataverse environment via the mcp-dataverse server.
|
|
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">
|
|
158
|
+
<entity name="privilege">
|
|
159
|
+
<attribute name="name"/>
|
|
160
|
+
<link-entity name="roleprivileges" from="privilegeid" to="privilegeid" alias="rp">
|
|
161
|
+
<attribute name="privilegedepthmask"/>
|
|
162
|
+
<filter>
|
|
163
|
+
<condition attribute="roleid" operator="eq" value="${e}"/>
|
|
164
|
+
</filter>
|
|
165
|
+
</link-entity>
|
|
166
|
+
</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">
|
|
168
|
+
<entity name="fieldpermission">
|
|
169
|
+
<attribute name="entityname"/>
|
|
170
|
+
<attribute name="attributelogicalname"/>
|
|
171
|
+
<attribute name="canread"/>
|
|
172
|
+
<attribute name="canupdate"/>
|
|
173
|
+
<attribute name="cancreate"/>
|
|
174
|
+
<filter>
|
|
175
|
+
<condition attribute="fieldsecurityprofileid" operator="eq" value="${e}"/>
|
|
176
|
+
</filter>
|
|
177
|
+
<order attribute="entityname" descending="false"/>
|
|
178
|
+
<order attribute="attributelogicalname" descending="false"/>
|
|
179
|
+
</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.
|
|
158
181
|
|
|
159
182
|
## Recommended Workflow
|
|
160
183
|
1. Start with dataverse_whoami to verify authentication.
|
|
@@ -181,11 +204,11 @@ ${n.map(r=>{let s=r.id??`Area_${++i}`,l=r.groups.map(o=>{let c=o.id??`Group_${++
|
|
|
181
204
|
- Avoid retrieving more than 5000 records unless explicitly needed (use dataverse_retrieve_multiple_with_paging with maxTotal).
|
|
182
205
|
- Use server-side filtering ($filter / FetchXML conditions) instead of client-side filtering.
|
|
183
206
|
- Cache table metadata \u2014 it rarely changes during a session.
|
|
184
|
-
`;function
|
|
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}...
|
|
185
208
|
`),i.getToken().then(()=>{process.stderr.write(`[mcp-dataverse] Authenticated \u2713
|
|
186
|
-
`)}).catch(r=>{let
|
|
187
|
-
`)}),await t(()=>
|
|
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
|
|
188
211
|
`),i.getToken().then(()=>{process.stderr.write(`[mcp-dataverse] Authenticated \u2713
|
|
189
|
-
`)}).catch(
|
|
190
|
-
`)})}}async function
|
|
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("update")){let{runUpdate:a}=await import("./update-B3OXP6BV.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)}
|
|
191
214
|
`),process.exit(1)});
|