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.
npx claudepluginhub nickmeron/dataverse-mcp-serverThis skill uses the workspace's default tool permissions.
The user wants to scaffold or build a Dynamics 365 plugin project.
Provides core rules and tool routing for Dataverse, Dynamics 365, and Power Platform tasks: enforces workspace init check, Python-only scripting, and MCP→SDK→Web API prioritization.
Adds Dataverse tables to Power Apps code apps with generated TypeScript models and services. Creates new tables via API. Use for connecting to Dataverse, schema design, or querying data.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
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 > 1