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:

  1. Create a Menu Item (Action) in the AOT.
  2. Point the Menu Item to MyInventoryCheckTool.
  3. 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

Popular posts from this blog

Ledger Dimension Facade Class

Integration Capabilities and Support in Microsoft Dynamics 365 Finance & Operations (F&O) - An Overview

Performance and Monitoring in dynamics 365 F&O.