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

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

Ledger Dimension Facade Class

Performance and Monitoring in dynamics 365 F&O.