Estimated reading time: 4 minutes
Understand the engineering decisions behind Dataverse Skills and learn to build your own custom skills for organizational patterns. This advanced guide dissects both skill file formats (Microsoft’s table format and extended YAML frontmatter), explains how AI agents select and chain skills, and reveals the three-tool strategy (MCP Server, Python SDK, PAC CLI) that optimizes performance and cost. Includes production-tested examples for automating environment provisioning and integrating agent-built solutions into Azure DevOps ALM pipelines. Essential reading for solution architects extending Dataverse Skills for enterprise use.
Table of contents
- Introduction
- The Anatomy of a Skill File
- How Agents Select and Chain Skills
- The Three-Tool Strategy
- Deep Dive: The Dataverse Python SDK in Skills
- Deep Dive: MCP Tools Available to Skills
- Deep Dive: PAC CLI Operations in Skills
- Writing Your Own Custom Skills
- Testing and Validating Custom Skills
- Real-World Custom Skill Examples
- References & Further Reading
Introduction
In Part 1 – From Scripts to Intent: How Dataverse Skills Redefines Enterprise Development and Part 2 – Getting Started with Dataverse Skills: Install, Configure, and Build Your First Agent-Driven Solution, we covered the paradigm shift and the practical setup. Now, however, we go a level deeper—into the engineering decisions that make Dataverse Skills work. More importantly, we’ll explore the design patterns that make it extensible.
Understanding how Dataverse Skills work internally — the skill file formats, how agents select tools, how the Python SDK and MCP server interact — gives you two strategic advantages:
- Organizational customization: You can write your own skills that encode your firm’s specific Dataverse patterns, naming conventions, and architectural standards—turning tribal knowledge into executable agent instructions
- Diagnostic capability: When an agent makes an unexpected decision (creating a table via Web API instead of PAC CLI, for example), you can trace the decision back to specific skill instructions and adjust accordingly
This article is deliberately technical. It assumes you’re comfortable reading Markdown and YAML, understand RESTful API design, and have debugged multi-step automation workflows before. The target reader is a senior Power Platform developer or solution architect who needs to either extend Dataverse Skills for enterprise use or evaluate its technical architecture for governance approval.
We will dissect actual skill file anatomy, decode the agent’s tool selection heuristics, and walk through building two production-grade custom skills: one for automating environment provisioning workflows, and one for integrating agent-built solutions into Azure DevOps ALM pipelines. By the end, you’ll understand not just what Dataverse Skills does, but how—and more importantly, how to make it do what your organization needs.

The Anatomy of a Skill File
Every Dataverse skill is a Markdown file with YAML frontmatter and instructional content. This is intentional — the format is readable by humans, editable in any text editor, and interpretable by any coding agent without compilation.
Microsoft’s Official Format: YAML Frontmatter
The official Microsoft Dataverse-skills repository uses YAML frontmatter at the top of each skill file. Here’s the actual format from the dv-metadata skill:
---
name: dv-metadata
description: >
Create and modify Dataverse tables, columns, relationships, forms, and views using the Python SDK and Web API.
Use when: "add column", "create table", "add relationship", "lookup column", "create form",
"create view", "modify form", "FormXml", "SavedQuery", "option set", "picklist",
"MetadataService", "EntityDefinitions".
Do not use when: reading/writing data records (use dv-python-sdk),
exporting solutions (use dv-solution).
---
# Skill: Metadata — Making Changes
**Before the first metadata change in a session, confirm the target environment with the user.** See the Multi-Environment Rule in the overview skill for the full confirmation flow.
---
## How Changes Are Made: Environment-First
**Do not write solution XML by hand to create new tables, columns, forms, or views.**
The environment validates metadata far more reliably than an agent editing XML. The correct workflow is:
1. **Make the change in the environment** via the Dataverse MetadataService API (or `pac` commands where available)
2. **Pull the change into the repo** via `pac solution export` + `pac solution unpack`
3. **Commit the result**
The exported XML is generated by Dataverse itself and is always valid. Hand-written XML is fragile — a single incorrect attribute or missing element causes an import failure with an opaque error.
## Creating a Table
**ALWAYS use the SDK unless you need full control over OwnershipType, HasActivities, or other advanced properties.** Do NOT use `requests` or `urllib` for table creation when the SDK can handle it.
**SDK approach (use this by default):**
\```python
from auth import get_credential, load_env
from PowerPlatform.Dataverse.client import DataverseClient
import os
load_env()
client = DataverseClient(os.environ["DATAVERSE_URL"], get_credential())
info = client.tables.create(
"new_ProjectBudget",
{"new_Amount": "decimal", "new_Description": "string"},
solution="MySolution",
primary_column="new_Name",
)
print(f"Created: {info['table_schema_name']}")
\```Key characteristics of Microsoft’s format:
- YAML frontmatter: Uses standard YAML with
---delimiters - Multiline description: The
>character enables clean multiline descriptions - Embedded triggers: “Use when” patterns are quoted phrases the agent should recognize (e.g.,
"add column","create table") - Disambiguation guidance: “Do not use when” directs agents to other skills, preventing incorrect skill selection
- Natural flow: After the frontmatter, the skill body provides detailed instructions using standard Markdown
Extended Format: Additional Metadata Fields
For organizational skills or when you need explicit security boundaries and tool restrictions, you can extend the YAML frontmatter with additional fields:
---
name: aidevme-create-table
description: >
Creates a new custom table in Dataverse with the specified schema.
Use when: "create a table", "add an entity", "I need a new table for",
"create custom table", "new Dataverse table".
Do not use when: modifying existing tables (use dv-metadata),
creating virtual tables (requires connector configuration).
version: 1.0.0
phase: build
allowed-tools:
- bash
- web_fetch
- file
safety:
- Verify the publisher prefix matches the active solution before creating
- Check for existing tables with the same logical name
- Never create tables in the default solution
- Warn if table name conflicts with a system table
requires:
- dataverse-connect
- dataverse-create-solution
priority: normal
---
## Purpose
Creates a Dataverse table using the Web API. Handles all column types,
ownership modes, and automatically adds the table to the active solution.
## When to use
Use this skill when the user describes needing a new data entity.
You do not need to be asked to "use the create-table skill" —
infer when a table is needed from context.
## Pre-conditions
1. Active PAC CLI auth profile exists (`pac org who` succeeds)
2. Target solution exists and is active
3. Publisher prefix is known (from solution metadata or user input)
## Steps
### 1. Validate publisher prefix
Run: `pac org who` to get the environment URL.
Check active solution via MCP: `describe_table("solution")`
Extract publisher prefix from solution metadata.
If publisher prefix is unknown, ask the user before proceeding.
### 2. Check for conflicts
Before creating, call:
- `list_tables` via MCP — scan for tables matching the intended logical name
- If a match exists, report it and ask the user whether to skip, update, or abort
### 3. Create the table via Web API
Use the Dataverse Web API endpoint:
POST https://{org}.crm.dynamics.com/api/data/v9.2/EntityDefinitions
Request body:
\```json
{
"@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata",
"SchemaName": "{PublisherPrefix}_{TableName}",
"DisplayName": { "LocalizedLabels": [{ "Label": "{Display Name}", "LanguageCode": 1033 }] },
"DisplayCollectionName": { "LocalizedLabels": [{ "Label": "{Plural Display Name}", "LanguageCode": 1033 }] },
"OwnershipType": "UserOwned",
"HasActivities": false,
"IsActivity": false
}
\```
### 4. Add to solution
After creation:
\```bash
pac solution add-solution-component \
--solutionUniqueName {SolutionName} \
--component {LogicalName} \
--componentType 1
\```
### 5. Confirm and report
Report the created table's:
- Schema name
- Logical name
- Solution membership
- Next suggested actions (add columns, create relationships)
## Safety checks
- NEVER create tables without a publisher prefix
- NEVER add tables to the Default solution
- If HasActivities is set to true, warn the user this cannot be changed after creation
- Data type selection is PERMANENT — warn before creating columns if the user seems uncertain
## Error handling
| Error | Cause | Resolution |
|-------|-------|------------|
| 400 SchemaName exists | Table already exists | Skip or report to user |
| 403 Forbidden | Missing system customizer role | Check environment permissions |
| 400 Invalid publisher | Publisher not registered | Create publisher first or correct the prefix |Required vs. Optional YAML Fields
Required fields (Microsoft’s base format):
name — Unique identifier for the skill. Use kebab-case naming: {publisher}-{action}-{subject} (e.g., dv-metadata, aidevme-create-environment)
description — Multi-line description that includes:
- What the skill does (first line, concise)
- “Use when:” Pattern triggers with quoted examples (e.g.,
"add column","create table") - “Do not use when:” Disambiguation guidance pointing to other skills
The description is what agents use to determine skill relevance. Write it from the agent’s perspective — what should trigger this skill vs. other skills?
Optional fields (for organizational extensions):
allowed-tools — Critical security boundary. Restricts which tools the agent can use for this skill. In traditional software, you control execution through code paths and privilege checks. With agent-driven development, you control execution through declarative tool restrictions.
This matters more than you might initially assume: a data-loading skill may legitimately allow bash (for running Python scripts) and file (for reading CSV imports), but allowing web_fetch opens the door to data exfiltration via external API calls. The allowed-tools field is your defense against prompt injection attacks that attempt to trick the agent into unauthorized operations.
safety — Explicit safety rules the agent must check before proceeding. These are not enforced programmatically — they are instructions the agent interprets. Well-written safety rules prevent destructive operations. Think of these as codified guardrails that encode your organization’s governance policies.
requires — Declares skill dependencies. The agent will load prerequisite skills before executing this one, ensuring the environment is in the right state. This creates a dependency graph that the agent traverses automatically.
phase — Categorizes the skill: connect (authentication/setup), build (schema changes), operate (data operations), or custom (organizational extensions).
priority — Controls skill selection when multiple skills match the same intent. Set to high to override built-in skills with your organization’s custom version.
version — Semantic version for tracking skill evolution. Useful when sharing skills across teams or publishing to internal marketplaces.
When to Use Extended Fields
Use the base format (name + description only) when:
- Creating skills that closely follow Microsoft’s patterns
- You want maximum simplicity and maximum agent compatibility
- Your skills don’t require explicit tool restrictions
- You’re contributing back to the Microsoft repository
Add extended fields when:
- You need explicit security boundaries (
allowed-tools) - You’re building organizational skills with complex dependencies (
requires) - You want semantic versioning and priority control
- You need to override or enhance Microsoft’s built-in skills (
priority) - You have strict governance requirements (
safety)
All fields work with GitHub Copilot and Claude Code (via .agent.md files) and Claude Desktop (via MCP skills). The agent interprets the YAML metadata and follows the instructional content in the body.

How Agents Select and Chain Skills
The selection logic is not rule-based in the traditional sense. The agent uses the name, description, and triggers fields across all installed skills to make a relevance judgment.
When you type a prompt like:
Create a table for tracking employee certifications
The agent:
- Scans all installed skills for relevance to the intent
- Identifies
dataverse-create-tableas the primary match - Checks its
requiresfield — loadsdataverse-connectanddataverse-create-solutionfirst - Executes the skill steps in sequence
- Dynamically selects tools within each step based on the
allowed-toolslist
Skill chaining in practice
For a complex prompt like the recruiting system example from Part 2, the agent chains skills in this order:
dataverse-connect
└── dataverse-mcp-register
dataverse-create-solution
dataverse-create-table (× 5, in parallel where safe)
└── dataverse-create-column (× N per table)
└── dataverse-create-relationship (× 2)
dataverse-load-data
└── dataverse-generate-sample-data
dataverse-query
The agent determines this chain from the requires declarations and the semantic content of the prompt — not from a hardcoded workflow
When skills conflict
If two skills match the same intent (for example, a custom aidevme-create-table and the built-in dataverse-create-table), the agent uses recency and specificity to choose: more specific skills (higher match on description and triggers) take precedence over more general ones. You can also set explicit priority in the frontmatter:
priority: high # Overrides built-in skills with the same intent

The Three-Tool Strategy
One of the most important—and least obvious—design decisions in Dataverse Skills is its tool selection strategy. Unlike traditional development where the developer decides which API to call, agent-driven development requires encoding that decision-making logic into the skills themselves. The skills must teach the agent not just how to use the Dataverse Web API, Python SDK, and PAC CLI, but when each is the optimal choice.
This isn’t just about performance optimization. It’s about cost management (MCP calls consume Copilot credits), operational correctness (bulk operations require transactional integrity), and architectural compliance (ALM operations must use PAC CLI to generate audit-trail-compatible solution exports).
The tool selection logic in Dataverse Skills reflects real-world production learnings—specifically, where early adopters made expensive mistakes by using the wrong tool for the job. The guidance below isn’t theoretical; it’s battle-tested.
Tool 1: Dataverse MCP Server
Best for:
- Fast, read-only metadata queries (
list_tables,describe_table) - Simple record reads (
read_query) - Single-record create/update when transactional integrity is not critical
Limitations:
- Charged per use (Copilot credits) for non-Copilot Studio clients
- Not optimal for bulk operations
- Limited to the tools exposed by the MCP server
Tool 2: Dataverse Python SDK
Best for:
- Bulk data operations (100+ records)
- Operations requiring Pandas DataFrames for transformation
- Data profiling and quality analysis
- ETL pipelines that write back to Dataverse
from PowerPlatform.Dataverse.client import DataverseClient
from azure.identity import AzureCliCredential
client = DataverseClient("https://yourorg.crm.dynamics.com", AzureCliCredential())
# Bulk create with native CreateMultiple (100x faster than looping)
records = [
{"aidevme_firstname": f"Consultant {i}", "aidevme_specialization": "Power Platform"}
for i in range(1, 51)
]
ids = client.records.create("aidevme_consultant", records)
print(f"Created {len(ids)} consultants")
# SQL query for analytical reads
results = client.query_sql(
"SELECT TOP 20 aidevme_projectname, aidevme_status "
"FROM aidevme_project "
"WHERE aidevme_status = 1 " # 1 = Active
"ORDER BY nextwit_createdon DESC"
)Tool 3: PAC CLI
Best for:
- Solution ALM (export, import, publish)
- Environment management
- Authentication lifecycle
- Component registration (adding tables/columns to solutions)
# Solution export for ALM
pac solution export \
--name ConsultingTracker \
--path ./solutions/ConsultingTracker.zip \
--managed false
# Publish all customizations
pac solution publishDecision matrix
The skills encode this decision logic explicitly:
| Task | MCP | Python SDK | PAC CLI |
| List tables | ✅ Primary | ❌ | ❌ |
| Read 10 records | ✅ Primary | ✅ | ❌ |
| Read 10,000 records | ❌ | ✅ Primary | ❌ |
| Create 1 record | ✅ | ✅ | ❌ |
| Create 500 records | ❌ | ✅ Primary | ❌ |
| Create a table schema | ❌ | ❌ | ✅ via Web API call |
| Add component to solution | ❌ | ❌ | ✅ Primary |
| Export solution | ❌ | ❌ | ✅ Primary |
| Authenticate | ❌ | ❌ | ✅ Primary |

Deep Dive: The Dataverse Python SDK in Skills
The Python SDK (PowerPlatform-Dataverse-Client, currently v0.1.0b7) is the workhorse for the Operate phase. Understanding its capabilities helps you write better skills and better prompts.
Authentication
The SDK uses Azure Identity providers — the same pattern as all modern Azure SDKs:
from azure.identity import (
AzureCliCredential, # Development: uses 'az login' session
InteractiveBrowserCredential, # Development: browser popup
ClientSecretCredential, # Production: service principal
CertificateCredential, # Production: certificate-based SPN
)
from PowerPlatform.Dataverse.client import DataverseClient
# For local development (preferred with Dataverse Skills)
credential = AzureCliCredential()
client = DataverseClient("https://yourorg.crm.dynamics.com", credential)CRUD operations
# Create single record
ids = client.records.create("aidevme_project", {
"aidevme_projectname": "Digital Transformation",
"aidevme_status": 1, # Active
"aidevme_budget": 450000.00
})
# Create multiple records (uses CreateMultiple — much faster)
ids = client.records.create("aidevme_project", [
{"aidevme_projectname": "Project Alpha", "aidevme_status": 1},
{"aidevme_projectname": "Project Beta", "aidevme_status": 2},
])
# Read with filter
for batch in client.records.get(
"aidevme_project",
filter="aidevme_status eq 1",
select=["aidevme_projectname", "aidevme_budget"]
):
for record in batch:
print(record["aidevme_projectname"])
# Update
client.records.update("aidevme_project", record_id, {
"aidevme_status": 3 # On Hold
})
# Delete
client.records.delete("aidevme_project", record_id)Pandas integration
For analytical use cases, the SDK wraps results in DataFrames:
import pandas as pd
# Get all projects as a DataFrame
df = client.records.get_dataframe(
"aidevme_project",
select=["aidevme_projectname", "aidevme_status", "aidevme_budget"]
)
# Analyze
print(df.groupby("aidevme_status")["aidevme_budget"].sum())
print(df.describe())
# Write analysis results back to Dataverse
summary_records = df.groupby("aidevme_status").agg(
total_projects=("aidevme_projectname", "count"),
total_budget=("aidevme_budget", "sum")
).reset_index().to_dict("records")
client.records.create("aidevme_portfoliosummary", summary_records)Current limitations (preview)
- General-purpose OData batching not fully supported
DeleteMultiplenot yet available (use individual deletes or Python loops)- Limited retry policy — only network errors trigger automatic retry
- No association operation support for relationships

Deep Dive: MCP Tools Available to Skills
When the Dataverse MCP server is connected, skills can call these built-in tools:
| Tool | Description | Example use |
| list_tables | Lists all tables in the environment | Environment discovery, schema auditing |
| describe_table | Returns schema for a specific table | Pre-build validation, column discovery |
| read_query | Executes a read query (OData or natural language) | Fetching records for business questions |
| create_record | Creates a single record | Quick single-record operations |
| update_record | Updates a single record | Status updates, field corrections |
| search_knowledge | Searches knowledge base content | RAG operations on Dataverse knowledge |
| list_prompts | Lists reusable prompts in the environment | Discovering Business Skills |
| execute_prompt | Executes a stored prompt | Running Business Skills from code |
Calling MCP tools from a skill
Within a skill file, MCP tool calls are described in natural language — the agent translates them to actual API calls:
## Steps
### 1. Verify table exists
Call MCP tool: `describe_table("aidevme_project")`
If the response is an error or empty, the table does not exist.
Proceed to create it. Otherwise, report the existing schema to the user
and ask whether to add columns, modify existing ones, or abort.
### 2. Query active records
Call MCP tool: `read_query` with filter:
"Show me all aidevme_project records where aidevme_status equals Active,
ordered by creation date descending, limit 20"
Deep Dive: PAC CLI Operations in Skills
The PAC CLI is called via bash commands within skills. Key patterns:
Solution management
# Create a solution with publisher
pac solution create \
--name ConsultingTracker \
--publisher-name AiDevMe \
--publisher-prefix aidevme \
--version 1.0.0.0
# Export (unmanaged, for source control)
pac solution export \
--name ConsultingTracker \
--path ./solutions/ \
--managed false
# Import to another environment
pac solution import \
--path ./solutions/ConsultingTracker.zip \
--activate-plugins true
# Publish all pending customizations
pac solution publish --async falseEnvironment operations
# List environments
pac env list --filter "Dev"
# Copy environment (for testing)
pac admin copy \
--source-url https://source.crm.dynamics.com \
--target-url https://target.crm.dynamics.comImportant PAC CLI note for skill authors
The skill documentation on the Dataverse Skills repository includes a critical warning:
Never use pac auth token — this command does not exist. Use Azure CLI instead:
az account get-access-token \
--resource "https://yourorg.crm.dynamics.com/" \
--tenant "your-tenant-id" \
--query accessToken \
-o tsvThis is the kind of tribal knowledge that makes skills valuable — encoding what not to do alongside what to do.

Writing Your Own Custom Skills
Creating a custom skill involves three steps: defining the metadata, writing the instruction body, and testing with your coding agent.
Skill file structure template
All Dataverse skills use YAML frontmatter. Start with the required fields (name, description) and add optional fields as needed:
Base format (required fields only):
---
name: {publisher}-{action}-{subject}
description: >
One or two sentences describing what this skill does and when to use it.
Use when: "trigger phrase 1", "trigger phrase 2", "natural language pattern".
Do not use when: {anti-patterns that should use other skills}.
---
# Skill: {Title}
## Purpose
What does this skill do? When should an agent invoke it?
## Pre-conditions
1. What must be true before this skill can run?
2. What environment state is assumed?
## Steps
### Step 1: Validate inputs
What to check first. Include error conditions.
### Step 2: Main operation
The actual work. Include tool calls, code examples, API payloads.
### Step 3: Verify and report
How to confirm success. What to report to the user.
## Error handling
| Error | Cause | Resolution |
|-------|-------|------------|
| Error description | Why it happens | How to fix it |
## Examples
### Example 1: Basic usage
Input: "..."
Expected agent behavior: "..."
Expected output: "..."Extended format (with optional fields):
---
name: {publisher}-{action}-{subject}
description: >
One or two sentences describing what this skill does and when to use it.
Use when: "trigger phrase 1", "trigger phrase 2", "natural language pattern".
Do not use when: {anti-patterns that should use other skills}.
version: 1.0.0
phase: connect | build | operate | custom
allowed-tools:
- bash
- file
- web_fetch # Only if needed
safety:
- List explicit safety rules here
- One rule per line
- Be specific about what must be checked before proceeding
requires:
- dataverse-connect # Almost always required
- list-other-prerequisites
priority: normal # or high to override built-in skills
---
## Purpose
What does this skill do? When should an agent invoke it?
## Pre-conditions
1. What must be true before this skill can run?
2. What environment state is assumed?
## Steps
### Step 1: Validate inputs
What to check first. Include error conditions.
### Step 2: Main operation
The actual work. Include tool calls, code examples, API payloads.
### Step 3: Verify and report
How to confirm success. What to report to the user.
## Error handling
| Error | Cause | Resolution |
|-------|-------|------------|
| Error description | Why it happens | How to fix it |
## Examples
### Example 1: Basic usage
Input: "..."
Expected agent behavior: "..."
Expected output: "..."Naming conventions
Following the repository’s naming pattern:
{publisher-prefix}-{verb}-{noun}
# Examples:
aidevme-create-environment
aidevme-export-solution-bundle
aidevme-dv-provision-dev-environment
aidevme-validate-publisher-prefix
Testing and Validating Custom Skills
Register your skill locally
For Claude Code, add your skill to the .claude-plugin directory in your repository:
your-project/
└── .claude-plugin/
├── marketplace.json
└── skills/
└── aidevme-create-environment.md
The marketplace.json file registers your skills:
{
"name": "AiDevMe Power Platform Skills",
"version": "1.0.0",
"publisher": "aidevme",
"skills": [
{
"name": "aidevme-create-environment",
"path": "skills/aidevme-create-environment.md"
}
]
}For GitHub Copilot, add to .github/plugins/:
your-project/
└── .github/plugins/
└── aidevme/
└── skills/
└── aidevme-create-environment.md
Test with explicit skill invocation
During development, invoke skills explicitly to test them in isolation:
# Claude Code
/aidevme-create-environment Create a new developer sandbox environment
named "Project Alpha Dev" with the Consulting Tracker solution installed.
Debugging skill selection
If the agent is not selecting your skill when you expect it to:
- Check that the
descriptioncontains the natural language patterns the user is likely to type - Add explicit
trigger phrasesin the skill body - Increase
priorityin the frontmatter if competing with built-in skills - Verify the skill file is correctly registered in
marketplace.json
Real-World Custom Skill Examples
The following examples are from Daniel Kerridge’s claude-code-power-platform-skills repository — a collection of production-ready skills for Power Platform development with Claude Code. These skills demonstrate how to encode complex domain knowledge into reusable, agent-executable instructions.
Example 1: Dataverse Plugins Skill
This skill guides the agent through developing, registering, and deploying Dataverse plugins — C# server-side extensions that execute custom business logic in the Dataverse execution pipeline.
Source: dataverse-plugins/SKILL.md
---
name: dataverse-plugins
description: >
Use when developing, registering, or deploying Dataverse plugins (C# server-side extensions).
Covers the IPlugin interface, execution pipeline stages, entity images, common patterns
(auto-numbering, cascading updates, validation), and registration/deployment.
Triggers on: "plugin", "server-side logic", "business logic", "auto-number",
"cascading update", "pre-operation", "post-operation", "plugin registration",
"IPlugin", "execution pipeline", "plugin trace", "InvalidPluginExecutionException",
"PreValidation", "PostOperation".
license: MIT
compatibility: "Dataverse SDK, .NET 4.6.2+, Plugin Registration Tool"
metadata:
author: custom
version: "1.0.0"
platform: "Microsoft Power Platform / Dataverse"
---
# Dataverse Plugins Skill
You are an expert in developing, registering, and deploying Dataverse plugins
— C# server-side extensions that execute custom business logic in response to
data operations (create, update, delete, retrieve, etc.) in the Dataverse
execution pipeline.
## CRITICAL RULES
1. **Plugins run in a sandbox** by default. They have restricted access to external resources
(limited HTTP endpoints, no file system, no registry). Plan accordingly.
2. **2-minute timeout** for synchronous plugins. Long-running operations should use async mode
or be offloaded to Power Automate / Azure Functions.
3. **Throw `InvalidPluginExecutionException`** to show user-facing errors. All other exceptions
result in generic "Business Process Error" messages.
4. **Never use static variables** for state. Plugin instances are cached and reused across
requests. Use `IPluginExecutionContext.SharedVariables` for pipeline-scoped state.
5. **Always register entity images** when you need pre/post field values. Don't make extra
Retrieve calls when an image would suffice.
6. **Test with Plugin Trace Log** enabled. Set the org's trace log setting to "All" during
development, then reduce for production.
## Quick Reference
| Concept | Details |
|---|---|
| Interface | `Microsoft.Xrm.Sdk.IPlugin` |
| Entry point | `Execute(IServiceProvider serviceProvider)` |
| Error handling | Throw `InvalidPluginExecutionException` |
| Timeout | 2 minutes (sync), 24 hours (async) |
| Isolation | Sandbox (default) or None (on-premises only) |
| Assembly size | 16MB max |
| Registration | Plugin Registration Tool (PRT) or pac CLI |
## Resource Files
- `resources/plugin-anatomy.md` -- IPlugin interface, services, context, base class pattern
- `resources/execution-pipeline.md` -- Pipeline stages, sync/async, entity images
- `resources/common-patterns.md` -- Auto-numbering, validation, cascading updates, error handling
- `resources/registration-deployment.md` -- PRT, pac CLI, step registration, debuggingKey features of this skill:
- Critical rules section — Encodes non-obvious constraints (sandbox limitations, timeout rules, error handling patterns) that prevent common mistakes
- Quick reference table — Provides at-a-glance lookup for plugin fundamentals
- Resource files — Breaks domain knowledge into focused sub-documents, keeping the main skill file concise while providing deep expertise when needed
- Comprehensive trigger phrases — Ensures the skill activates on both technical terms (“IPlugin”, “PreValidation”) and natural language (“server-side logic”, “business logic”)
Example 2: Dataverse Web Resources Skill
This skill teaches the agent to create, deploy, and manage Dataverse web resources — JavaScript, HTML, CSS, and other web content used to extend model-driven apps.
Source: dataverse-web-resources/SKILL.md
---
name: dataverse-web-resources
description: >
Use when creating, deploying, or managing Dataverse web resources for model-driven apps.
Covers JavaScript form scripts (OnLoad, OnSave, OnChange events), HTML dashboard pages,
CSS styling, image resources, navigation/side panes, ribbon/command bar customization,
business process flow client API, and deployment via the Web API. Triggers on:
"web resource", "javascript form", "form script", "html dashboard", "ribbon command",
"onload event", "onchange event", "onsave event", "formContext", "Xrm.WebApi",
"web resource deployment", "dashboard page", "form event handler", "side pane",
"command bar", "ribbon", "modal dialog", "navigation", "Xrm.App", "Xrm.Navigation",
"business process flow", "bpf", "stage change".
license: MIT
compatibility: "Dataverse Web API v9.2, Model-Driven Apps"
metadata:
author: custom
version: "1.0.0"
platform: "Microsoft Power Platform / Dataverse"
---
# Dataverse Web Resources Skill
You are an expert in creating, deploying, and using Dataverse web resources within
model-driven apps. Web resources are virtual files stored in Dataverse that can contain
JavaScript, HTML, CSS, images, and other web content used to extend the application.
## CRITICAL RULES
1. **Always use a publisher prefix namespace** for web resource names (e.g., `cnt_/js/formscript.js`).
The forward slash creates a virtual folder structure.
2. **JavaScript must use the namespace pattern.** Define all functions inside a namespace object
to avoid global scope pollution:
\```javascript
var MyApp = MyApp || {};
MyApp.FormScripts = { onLoad: function(executionContext) { ... } };
\```
3. **Always pass `executionContext`** to form event handlers. Enable "Pass execution context
as first parameter" when registering. Then: `var formContext = executionContext.getFormContext();`
4. **Content must be base64-encoded** when creating via the API. Use PowerShell's
`[Convert]::ToBase64String()` or equivalent.
5. **Always publish after creating/updating** web resources. They remain in draft until published.
6. **5MB size limit** per web resource (configurable by org admin). Minify large JS/CSS.
7. **Always consult `resources/ux-decision-guide.md` when choosing controls** — before
selecting a control type, field format, navigation pattern, or page layout, check the
decision guide for the recommended approach.
8. **`Xrm` is NOT available in web resources loaded via MDA sitemap.** Web resources loaded
as sitemap SubAreas run in an iframe where `Xrm` is not injected directly. Use this
fallback chain: (1) `Xrm.Utility.getGlobalContext()`, (2) `parent.Xrm.Utility.getGlobalContext()`,
(3) `WhoAmI` API call (`GET /api/data/v9.2/WhoAmI`) for user identity. Cache the result.
## Quick Reference
| Operation | Method | Endpoint |
|---|---|---|
| Create web resource | POST | `/webresourceset` |
| Update web resource | PATCH | `/webresourceset({id})` |
| Delete web resource | DELETE | `/webresourceset({id})` |
| Add to solution | Action | `AddSolutionComponent` (ComponentType=61) |
| Publish | Action | `PublishXml` |
## Xrm Client API Quick Reference
| API | Purpose | Target |
|---|---|---|
| `Xrm.App.sidePanes.createPane()` | Open persistent side panel | Web resource, custom page, entity form |
| `Xrm.Navigation.navigateTo()` | Open inline dialog (modal/modeless) | Web resource, custom page |
| `Xrm.Navigation.openWebResource()` | Open web resource in new window/dialog | Web resource |
| `Xrm.WebApi.createRecord()` | Create record from form JS | Dataverse table |
| `formContext.data.process.moveNext()` | Navigate BPF stages | Form process |
| `Xrm.Utility.getResourceString(webresource, key)` | Get localized string from RESX | Web resource |
## Resource Files
- `resources/types-reference.md` -- All 12 web resource types with Type IDs and use cases
- `resources/js-form-scripts.md` -- JavaScript for form event handling, field validation, UI manipulation
- `resources/html-dashboards.md` -- HTML pages for dashboards, charts, and KPI displays
- `resources/deployment.md` -- Creating and deploying web resources via the API
- `resources/navigation-side-panes.md` -- Side panes, dialogs, navigation APIs (Xrm.App, Xrm.Navigation)
- `resources/ribbon-command-bar.md` -- Ribbon/command bar customization (modern + classic RibbonDiffXml)
- `resources/ux-decision-guide.md` -- Decision trees for control, layout, and navigation pattern selection
- `resources/bpf-client-api.md` -- Business Process Flow JavaScript API, events, stage navigationKey features of this skill:
- Platform-specific constraints — Critical rules address web resource quirks that aren’t documented well elsewhere (namespace requirements,
Xrmavailability in sitemap SubAreas, base64 encoding) - Dual quick reference tables — Separates Web API operations from Client API patterns for faster lookup
- Extensive resource files — 8 focused reference documents covering different web resource scenarios (form scripts, dashboards, navigation, BPF, ribbon customization)
- Rich trigger vocabulary — 30+ trigger phrases covering both technical APIs (
Xrm.WebApi,formContext) and natural language (“form script”, “side pane”, “command bar”)
Why these examples matter:
Both skills demonstrate production-grade skill design — they don’t just tell the agent what to do, they encode the why through critical rules, prevent common mistakes through safety guardrails, and provide quick-reference tables for efficient execution. The pattern of separating foundational rules (in the main skill) from detailed implementation patterns (in resource files) keeps skills maintainable while providing deep expertise when needed.
You can explore the complete skill collection at github.com/DanielKerridge/claude-code-power-platform-skills.
Frequently Asked Questions
Which skill format should I use — table or YAML frontmatter?
Both work. Use the table format if you’re contributing to the Microsoft repository or want maximum simplicity. Use YAML frontmatter when you need explicit tool restrictions (allowed-tools), dependency chains (requires), or priority control. For organizational skills that need security boundaries, YAML frontmatter is the better choice.
Do skill files need to be valid Markdown?
Yes, but the bar is low. Standard Markdown rendering is expected, but agents primarily process the raw text. The most critical parts are the YAML frontmatter (which must be valid YAML) and the content structure (headings, code blocks). Minor Markdown formatting issues will not prevent the skill from working.
Can I use Python in skill files?
Yes. Code blocks in any language are interpreted as instructions to the agent. If you include a Python code block, the agent will generate and execute that code (subject to the allowed-tools permission including bash). If bash is allowed, the agent can run Python scripts by writing them to a temp file and executing them.
How long should a skill file be?
Best practice from the repository is to keep each skill focused on a single operation. If a skill file exceeds 200 lines, consider splitting it. The requires field handles dependencies — do not repeat shared logic across skills.
Can I share custom skills across my team?
Yes. Commit the .claude-plugin/ or .github/plugins/ directory alongside your solution source in version control. All developers cloning the repository get the same skills. This is one of the most powerful aspects of the format.
References & Further Reading
Dataverse Skills Repository
- microsoft/Dataverse-skills on GitHub
- Full skill catalog
- AGENTS.md — contributor conventions
- CONTRIBUTING.md
Python SDK Deep Dive
- Dataverse SDK for Python overview (Microsoft Learn)
- Build agentic flows with Dataverse SDK for Python
- Python SDK API reference
- Analyze and automate business data with Python SDK
- Python SDK examples on GitHub
