Robust X++ class skeleton - AI Custom API
Robust X++ class skeleton - AI Custom API
This
template demonstrates how to structure the class, define the input parameters
(Data Contract), and implement the logic required for the invoke_action tool to
function.
X++ Developer Starter Kit: AI Custom API
This example assumes you are building a tool to "Check
Inventory Stock" directly via code.
1. The Data Contract (The Input)
First, define what information the AI Agent needs to send you. This uses standard D365 F&O Data Contracts.
[DataContract]
public class MyInventoryCheckContract
{
str itemId;
str siteId;
[DataMember("ItemId"), SysOperationLabel("Item ID"),
SysOperationHelpText("The identifier for the product to check.")]
public str
parmItemId(str _itemId = itemId)
{
itemId =
_itemId;
return itemId;
}
[DataMember("SiteId"), SysOperationLabel("Site ID"),
SysOperationHelpText("The specific warehouse site.")]
public str
parmSiteId(str _siteId = siteId)
{
siteId =
_siteId;
return siteId;
}
}
2. The Main Class (The Logic)
This is the class that implements ICustomAPI. It acts as the bridge between the MCP Server and your business logic.
using Microsoft.Dynamics.ApplicationPlatform.Environment;
// 1. Attribute to identify this as an AI-enabled action (if
applicable in your version)
// 2. Implement the ICustomAPI interface
public class MyInventoryCheckTool implements ICustomAPI
{
///
<summary>
/// This method
defines the inputs the Agent needs to provide.
/// It returns the
Contract class we defined above.
///
</summary>
public Object
getContract()
{
return new
MyInventoryCheckContract();
}
///
<summary>
/// This is the
core logic engine.
/// The Agent
passes the contract here, and you return the result.
///
</summary>
public str
invoke(Object _contract)
{
MyInventoryCheckContract contract = _contract as
MyInventoryCheckContract;
str result;
// ---
VALIDATION LAYER ---
if
(!contract.parmItemId())
{
throw
error("Item ID is required.");
}
// ---
BUSINESS LOGIC LAYER ---
// Example:
querying the InventSum table directly for speed
InventSum
inventSum;
InventDim
inventDim;
select
sum(AvailPhysical) from inventSum
exists
join inventDim
where
inventDim.inventDimId == inventSum.inventDimId
&&
inventDim.InventSiteId == contract.parmSiteId()
&&
inventSum.ItemId == contract.parmItemId();
// ---
RESPONSE LAYER ---
// Format the
output clearly so the LLM can read it easily
result =
strFmt("Stock Status: Found %1 physically available units of Item '%2' at
Site '%3'.",
inventSum.AvailPhysical,
contract.parmItemId(),
contract.parmSiteId());
return result;
}
///
<summary>
/// Provides a
description of what this tool does for the AI's 'find_actions' capability.
///
</summary>
public str
getDescription()
{
return
"Checks the physical available inventory for a specific item at a specific
site. Use this when the user asks for stock levels.";
}
}
3. The Final Step: Security Association
To make this code visible to the find_actions tool, remember
the architecture rule mentioned in the blog post:
- Create
a Menu Item (Action) in the AOT.
- Point
the Menu Item to MyInventoryCheckTool.
- Assign
the Menu Item to a Privilege/Role that the AI Agent's user account
holds.
Without this step, find_actions will return an empty list,
even if your code is perfect.

Comments
Post a Comment