Scaffolds, builds, signs, and deploys Dynamics 365 plugin projects using PAC CLI and .NET. Provides templates for plugin classes and pre/post image access. Useful for new plugin projects, DLL builds, or assembly signing.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dynamics365-dataverse:pac-pluginThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The user wants to scaffold or build a Dynamics 365 plugin project.
The user wants to scaffold or build a Dynamics 365 plugin project.
Argument provided: $ARGUMENTS
pac --version
dotnet --version # .NET 6+ SDK required
pac plugin init --outputDirectory ./MyPlugin
cd ./MyPlugin
dotnet build
This scaffolds:
.csproj with Microsoft.CrmSdk.CoreAssemblies referencePlugin1.cs — starter plugin class implementing IPlugin.snk — strong name key file for assembly signingStandard pattern:
using Microsoft.Xrm.Sdk;
using System;
namespace MyPlugin
{
public class PreCreateAccount : IPlugin
{
// Unsecure/Secure configuration from step registration
private readonly string _unsecureConfig;
private readonly string _secureConfig;
public PreCreateAccount(string unsecureConfig, string secureConfig)
{
_unsecureConfig = unsecureConfig;
_secureConfig = secureConfig;
}
public void Execute(IServiceProvider serviceProvider)
{
// Get context
var context = (IPluginExecutionContext)serviceProvider
.GetService(typeof(IPluginExecutionContext));
// Get tracing service (writes to Plugin Trace Logs)
var tracingService = (ITracingService)serviceProvider
.GetService(typeof(ITracingService));
// Get org service (for CRUD operations)
var serviceFactory = (IOrganizationServiceFactory)serviceProvider
.GetService(typeof(IOrganizationServiceFactory));
var orgService = serviceFactory.CreateOrganizationService(context.UserId);
try
{
tracingService.Trace("Plugin started for message: {0}", context.MessageName);
if (context.InputParameters.Contains("Target")
&& context.InputParameters["Target"] is Entity entity)
{
// Your logic here
tracingService.Trace("Entity: {0}, Id: {1}", entity.LogicalName, entity.Id);
}
}
catch (Exception ex)
{
tracingService.Trace("Error: {0}", ex.ToString());
throw new InvalidPluginExecutionException(
$"An error occurred in {nameof(PreCreateAccount)}: {ex.Message}", ex);
}
}
}
}
// Pre-image (entity state BEFORE the operation)
if (context.PreEntityImages.Contains("PreImage"))
{
var preImage = context.PreEntityImages["PreImage"];
var oldName = preImage.GetAttributeValue<string>("name");
}
// Post-image (entity state AFTER the operation)
if (context.PostEntityImages.Contains("PostImage"))
{
var postImage = context.PostEntityImages["PostImage"];
var newName = postImage.GetAttributeValue<string>("name");
}
dotnet build -c Release
Output: bin/Release/net462/MyPlugin.dll (or netcoreapp3.1 for newer SDK)
For managed identity or strong naming:
# With signtool (Windows SDK)
signtool sign /f certificate.pfx /p "password" /fd SHA256 bin/Release/net462/MyPlugin.dll
# Verify signature
signtool verify /pa bin/Release/net462/MyPlugin.dll
For strong naming only (included in pac plugin init):
# The .snk file is auto-generated by pac plugin init
# It's referenced in the .csproj:
# <SignAssembly>true</SignAssembly>
# <AssemblyOriginatorKeyFile>YourKey.snk</AssemblyOriginatorKeyFile>
After building, use the MCP tools:
Upload assembly manually via Plugin Registration Tool (the classic way)
OR use pac plugin push for quick dev iteration:
pac plugin push --assemblyPath bin/Release/net462/MyPlugin.dll
Register steps using MCP tools:
list_plugin_assemblies → find your assemblylist_plugin_types → find your classlist_sdk_messages → find "Create"/"Update"/etclist_sdk_message_filters → find the entity filterregister_processing_step → register the stepregister_step_image → add pre/post imagesCommon packages:
# Core SDK (already included)
dotnet add package Microsoft.CrmSdk.CoreAssemblies
# For early-bound classes
dotnet add package Microsoft.CrmSdk.CoreTools
# For workflow activities
dotnet add package Microsoft.CrmSdk.Workflow
# For managed identity (token acquisition)
dotnet add package Microsoft.PowerPlatform.Dataverse.Client
# Using pac
pac modelbuilder build --outdirectory ./EarlyBound --entitynamesfilter "account;contact;opportunity"
# Or using CrmSvcUtil (classic)
CrmSvcUtil.exe /url:https://yourorg.crm.dynamics.com/XRMServices/2011/Organization.svc /out:EarlyBound.cs
MyPlugin/
├── MyPlugin.csproj # Project file with SDK references
├── MyPlugin.snk # Strong name key
├── Plugin1.cs # Starter plugin class
├── PreCreateAccount.cs # Your custom plugin
├── PostUpdateContact.cs # Another plugin class
├── Helpers/
│ └── TraceHelper.cs # Shared utilities
└── bin/
└── Release/
└── net462/
└── MyPlugin.dll # Built assembly
IPlugin.csproj has <SignAssembly>true</SignAssembly> and .snk existsIManagedIdentityService for external callscontext.Depth and exit if > 1npx claudepluginhub nickmeron/dataverse-mcp-serverGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.