One worked example per command. Setup lives in SETUP.md. Every example assumes d365fo is on PATH and the index is populated.
Output: --output json returns { "ok": true, "data": {…} } or { "ok": false, "error": { "code": "…", "message": "…", "hint": "…" } } on every command.
d365fo search class CustSame pattern for search table|edt|enum|form|query|view|entity|report|service|workflow|label.
d365fo get table CustTable
d365fo get table CustTable --resolve-labels # rewrite @File:Id tokens to textSupports get class|edt|enum|form|menu-item|security|label|role|duty|privilege|query|view|entity|report|service|service-group. Unknown names return a *_NOT_FOUND envelope with a hint: "Did you mean: …".
d365fo find coc CustTable::validateWriteAlso available: find relations|usages|extensions|handlers. find refs --xref queries DYNAMICSXREFDB for path/line/column/kind.
find refs <needle> searches inside X++ method bodies. By default it scans the source on disk on demand; if you extracted with --index-source it answers from the MethodSourceFts full-text index instead (much faster, and the result carries via: "fts"):
d365fo index extract --index-source # one-time: full-text index method bodies
d365fo find refs CustPostInvoice # now served from FTSd365fo find form-patterns --output json # full histogram
d365fo find form-patterns --pattern SimpleList # all SimpleList forms
d365fo find form-patterns --table CustTable # forms using CustTable
d365fo find form-patterns --similar-to CustGroup # same pattern + datasourceUse before generate form to confirm the right pattern.
d365fo form-pattern spec --output json # list all patterns + sub-patterns
d365fo form-pattern spec DetailsMaster --output json # full structural spec
d365fo form-pattern spec FieldsFieldGroups --output json # container sub-pattern specReturns the machine-readable spec the form-pattern validator enforces: required
structure tree (control types, occurrence, order), known PatternVersions,
when-to-use criteria, Microsoft reference forms, and lifecycle guidance.
Pure catalog data — needs no index. Free-text aliases (master, toc,
transaction, …) resolve to the canonical pattern.
d365fo get batch table:CustTable class:CustTableType edt:CustAccount --output jsonUp to 10 <kind>:<name> specs per call. Each item carries its own ok/error
— one failed lookup never fails the batch. Replaces the MCP server's
batch_get_info and collapses N get object round-trips into one process.
d365fo labels resolve @SYS12345 --lang en-US,csd365fo read class CustTable_Extension --method validateWriteExtread table and read form work the same way; add --lines 10-40 or --declaration to scope the snippet.
d365fo models list
d365fo models deps ApplicationSuite
d365fo models coupling --top 10
d365fo models coupling --only-cyclesmodels list enumerates every model with publisher, layer, and custom-flag. models coupling runs Tarjan SCC over ModelDependencies to surface dependency cycles and ranks every model by fan-in / fan-out / instability (I = Ce / (Ca + Ce), where 0 = most stable, 1 = most volatile).
# Business events
d365fo search business-event CustomerPayment --output json
d365fo get business-event CustPaymentBusinessEvent --output json
# Security policies (XDS)
d365fo search security-policy CustCustom --output json
d365fo get security-policy CustCustomSecurityPolicy --output json
# Configuration keys
d365fo search configuration-key TradeAgreements --output json
# Tiles and workspaces
d365fo search tile CustCustom --output json
d365fo search workspace VendPayment --output jsond365fo search any CustTableUNIONs every indexed kind in one query and returns byKind counts for triage.
d365fo stats --top 10Returns per-model object counts plus top tables (by field count), top classes (by method count), and top CoC extension targets. Handy for sizing a customisation and for agent prompts.
d365fo index refresh
d365fo index refresh --force # re-scan every model
d365fo index extract --since 2026-04-01T00:00:00Z # explicit threshold
d365fo index history --limit 20 # last 20 per-model extract runs
d365fo index history --model Contoso # filter to one modelindex refresh compares each model's content fingerprint against the DB row and re-extracts only changed models. --force ignores all thresholds.
d365fo lint
d365fo lint --category table-no-index,string-without-edt --all-models
d365fo lint --category today-usage,do-insert-update,doc-comment-missing
d365fo lint --format sarif > lint.sarif16 rule categories (see ARCHITECTURE.md for the full list):
| Category | What it finds |
|---|---|
table-no-index |
Tables without cluster/alternate index |
string-without-edt |
String fields without an EDT |
insert-in-loop |
.insert() inside a loop — suggest RecordInsertList |
nested-select |
Nested while select loops |
force-literals |
forceLiterals in a select — SQL injection risk |
tts-try-catch |
try inside ttsbegin/ttscommit |
runbase-no-can-go-batch |
RunBaseBatch subclass without canGoBatch() |
doc-comment-missing |
Public/protected methods without /// <summary> |
| … | (and 8 more — see ARCHITECTURE.md) |
Defaults to custom models only; --all-models includes ISV/MS content. --format sarif emits SARIF 2.1.0 for CI (GitHub code-scanning, Azure DevOps).
d365fo validate name Table FmVehicle --prefix Fm
d365fo validate name Coc CustTable_ExtensionStatic naming-rule check (publisher prefix, PascalCase, suffix conventions). Returns structured violations[] with code, severity, message. Useful as a pre-commit hook.
d365fo init # in a terminal: interactive wizard
d365fo init --run-extract
d365fo init --dry-run # show what would be done
d365fo init --persist-profile # append env vars to $PROFILE / ~/.profileAuto-detects the Windows PackagesLocalDirectory, prepares the SQLite schema, and (with --run-extract) drives the full extract pipeline. --persist-profile writes D365FO_PACKAGES_PATH / D365FO_INDEX_DB (and, if set, D365FO_LABEL_LANGUAGES) to the user's shell profile.
In a real terminal, init is a wizard unless --packages, --output, --dry-run, or --no-wizard is given (or output is piped/non-TTY, as it always is for an agent's shell tool calls) — it asks for the packages path, an optional UDE extra root, label languages, and whether to persist / build the index now. --persist-profile and --run-extract still work as pre-answers to those last two questions when the wizard does run, so d365fo init --persist-profile above launches the wizard interactively but skips asking whether to persist.
generate writes atomically (.tmp + move) and keeps a .bak when --overwrite is used. Pass --install-to <Model> to drop the artefact straight into a model folder via the bridge (requires D365FO_BRIDGE_ENABLED=1, D365FO_PACKAGES_PATH, D365FO_BIN_PATH).
Add --verify to read the artefact back through the D365FO Metadata API after writing it, the way Visual Studio would. It applies to --install-to (the provider resolves objects by name inside the packages paths, so an arbitrary --out path is not something it can look up) and is skipped with a note when the runtime is unavailable — generation keeps working offline either way. A write that the provider then refuses to load fails with VERIFY_FAILED; the file is left on disk so you can open it in Visual Studio.
# Pattern-driven (P1) — emits canonical TableGroup, default fields, alt-key index
d365fo generate table FmCustomer \
--pattern master \
--label "@Fleet:Customer" \
--install-to FleetManagement
# Header / lines pair (worksheet pattern)
d365fo generate table FmOrderHeader --pattern worksheet-header --install-to FleetManagement
d365fo generate table FmOrderLine --pattern worksheet-line --install-to FleetManagement
# Temp table — TempDB is a TableType, not a TableGroup
d365fo generate table FmTmpStaging \
--pattern main --table-type TempDB \
--install-to FleetManagement
# Hand-picked fields override pattern defaults
d365fo generate table FmVehicle \
--label "@Fleet:Vehicle" \
--field VIN:VinEdt:mandatory \
--field Make:Name \
--primary-key VIN \
--out src/MyModel/AxTable/FmVehicle.xml
# Gate the table behind a configuration key and give it a drill-down form
d365fo generate table FmVehicle \
--pattern master \
--configuration-key FmModule \
--form-ref FmVehicleForm \
--install-to FleetManagementPatterns: main/master, transaction, parameter/setup/config, group, worksheetheader/header, worksheetline/line, reference/lookup, framework, miscellaneous. Temp tables: --table-type TempDB --pattern main.
--configuration-key and --form-ref are omitted from the XML when not passed, so the AOT defaults (ungated, no drill-down form) still apply.
d365fo generate class FmVehicleService --extends RunBase \
--out src/MyModel/AxClass/FmVehicleService.xmld365fo generate coc CustTable --method update --method insert \
--out src/MyModel/AxClass/CustTable_Extension.xmld365fo generate simple-list FmVehicleListPage --table FmVehicle \
--out src/MyModel/AxForm/FmVehicleListPage.xmlDeprecated alias. Use
generate form --pattern SimpleListinstead.
d365fo generate form FmVehicleDetails \
--pattern DetailsMaster \
--table FmVehicle \
--field VIN --field Make --field Model \
--caption "@Fleet:Vehicles" \
--out src/MyModel/AxForm/FmVehicleDetails.xml| Pattern | Use case |
|---|---|
SimpleList |
Setup / config tables |
SimpleListDetails |
List + detail panel |
DetailsMaster |
Full master record (CustTable shape) |
DetailsTransaction |
Header + lines — add --lines-table <TABLE> |
Dialog |
Modal popup |
TableOfContents |
Tabbed settings page |
Lookup |
Dropdown lookup |
ListPage |
Navigation list |
Workspace |
KPI tiles + panorama |
--field (repeatable) — grid/detail columns. --section Name:Caption — tabs / panorama sections. --lines-table <TABLE> — secondary datasource for DetailsTransaction.
Pattern gate: before writing, generate form validates the generated XML
against the form-pattern catalog (rules FP001–FP010). Structural violations
block the write while D365FO_FORM_PATTERN_ENFORCE=true (default); the JSON
summary carries a patternCheck block and any recommendations as warnings.
d365fo form-pattern validate src/MyModel/AxForm/FmVehicleDetails.xml --output json
cat FmVehicles.xml | d365fo form-pattern validate # stdin works tooExit codes: 0 clean (or warnings only), 2 structural errors. Run after any
hand edit of form XML; d365fo form-pattern spec <P> shows the required tree
when a violation needs fixing.
d365fo generate entity FmVehicleEntity --table FmVehicle \
--public-entity-name FmVehicle --public-collection-name FmVehicles \
--all-fields \
--out src/MyModel/AxDataEntityView/FmVehicleEntity.xmlEmits a single-datasource AxDataEntityView. --all-fields auto-populates fields from the source table (mandatory flag carries over). Without it <Fields /> is empty.
d365fo generate extension Table CustTable Contoso \
--out src/MyModel/AxTableExtension/CustTable.Contoso.xmlName is always <Target>.<Suffix> to match the AOT convention. Kinds: Table, Form, Edt, Enum.
d365fo generate event-handler Contoso_CustTable_Handler \
--source-kind Table --source-object CustTable --event inserted \
--out src/MyModel/AxClass/Contoso_CustTable_Handler.xmlPicks the right attribute ([DataEventHandler], [FormEventHandler], [FormDataSourceEventHandler], [SubscribesTo]) from --source-kind.
d365fo generate privilege FmVehicleReadPriv \
--entry-point FmVehicleListPage --entry-kind MenuItemDisplay --entry-object FmVehicleListPage \
--access Read --label "@Fleet:ReadVehicles" \
--out src/MyModel/AxSecurityPrivilege/FmVehicleReadPriv.xml
d365fo generate duty FmVehicleMaintainDuty \
--privilege FmVehicleReadPriv --privilege FmVehicleUpdatePriv \
--out src/MyModel/AxSecurityDuty/FmVehicleMaintainDuty.xml
# Scaffold-and-wire in one pass: emit the duty/privilege XML AND merge its
# name into an existing role's <Duties> / <Privileges>.
d365fo generate duty FmReportingDuty --privilege FmReportsViewPriv \
--out src/MyModel/AxSecurityDuty/FmReportingDuty.xml \
--into-role src/MyModel/AxSecurityRole/FmVehicleAdminRole.xmlBoth generate privilege and generate duty accept --into-role <PATH>; after the scaffold file is written, the role XML is updated idempotently with a .bak sibling (same merge path as generate role --add-to).
# Two-parameter batch job — emits contract, service, and controller XML
d365fo generate sysoperation ProcessVendorPayments \
--param "fromDate:TransDate" --param "toDate:TransDate" \
--execution-mode Asynchronous \
--out-contract c:/AOT/MyModel/AxClass/ProcessVendorPaymentsContract.xml \
--out-service c:/AOT/MyModel/AxClass/ProcessVendorPaymentsService.xml \
--out c:/AOT/MyModel/AxClass/ProcessVendorPaymentsController.xmlThe controller XML extends SysOperationServiceController. --param name:EdtType (repeatable) generates parmXxx() accessors decorated with [DataMemberAttribute] on the contract class. Use --execution-mode Synchronous|Asynchronous|ScheduledBatch.
# Module extension class + EDT + form handler
d365fo generate number-sequence CustCustomModule \
--edt CustCustomNum --scope Company --table CustCustomTable \
--out c:/AOT/MyModel/AxClass/NumberSeqModuleExtension_CustCustomModule.xml \
--out-edt c:/AOT/MyModel/AxEdt/CustCustomNum.xml \
--out-handler c:/AOT/MyModel/AxClass/CustCustomTable_NumberSeqFormHandler.xmlEmits a CoC extension of NumberSeqApplicationModule wired to the new EDT and an optional NumberSeqFormHandler extension class for the target form.
# Approval workflow with document class, approval element and submit stub
d365fo generate workflow PurchTableApproval \
--table PurchTable --approval-name PurchTableApprovalElement \
--out c:/AOT/MyModel/AxWorkflowTemplate/PurchTableApproval.xml \
--out-document c:/AOT/MyModel/AxClass/PurchTableWorkflowDocument.xml \
--out-approval c:/AOT/MyModel/AxWorkflowApproval/PurchTableApprovalElement.xml \
--out-submit c:/AOT/MyModel/AxClass/PurchTable_WorkflowSubmitExtension.xmlThe workflow type itself is an AxWorkflowTemplate — there is no AxWorkflow AOT folder.
--out-document emits a WorkflowDocument subclass. --approval-name / --task-name emit
AxWorkflowApproval / AxWorkflowTask elements and reference them from SupportedElements.
--out-submit emits a CoC extension of the target table that adds canSubmitToWorkflow().
The command reports the menu items, query and --category workflow category it references but
does not create — the model does not build until those exist.
d365fo generate menu-item CustCustomForm \
--kind Display --object CustCustomForm --object-type Form \
--label "@SYS12345" \
--out c:/AOT/MyModel/AxMenuItemDisplay/CustCustomForm.xml--kind accepts Display, Action, or Output. --object-type accepts Form, Class, or Report.
d365fo generate edt CustCustomId --base-type String --size 20 --label "@SYS100" \
--out c:/AOT/MyModel/AxEdt/CustCustomId.xml--base-type accepts String, Integer, Real, Int64, Date, UtcDateTime, Enum, Guid. --size applies to String EDTs. Use --extends <BaseEdt> to derive from an existing EDT.
d365fo generate enum CustCustomStatus \
--value "None:0:@SYS000" --value "Active:1:@SYS001" --value "Closed:2:@SYS002" \
--out c:/AOT/MyModel/AxEnum/CustCustomStatus.xml--value name:intValue:labelKey (repeatable). Adds <IsExtensible>Yes</IsExtensible> by default — pass --no-extensible to opt out.
d365fo generate query SalesTableWithLines \
--ds SalesTable --join "SalesLine:InnerJoin:SalesTable" \
--out c:/AOT/MyModel/AxQuery/SalesTableWithLines.xml--ds sets the root datasource. --join target:joinKind:parentDs (repeatable) adds embedded datasources. joinKind accepts InnerJoin, OuterJoin, ExistsJoin, NotExistsJoin.
d365fo generate view FmOpenRentals \
--query FmOpenRentalsQuery \
--field VehicleId:FmVehicle \
--field RentalId:FmRental:RecId \
--computed DaysOut:getDaysOutSQL:Int \
--install-to FleetManagement--field <name>:<dataSource>[:<dataField>] projects a query column (dataField
defaults to name). --computed <name>:<viewMethod>:<type> adds a field backed by
an X++ method returning SQL; the type (String|Int|Int64|Real|Date|UtcDateTime|Enum)
is required because the AOT encodes it in the field's discriminator and cannot infer
it from the method. --query is mandatory — a view without one has nothing to
project. No <Ranges> and no <Title*> are emitted: ranges belong on the backing
query.
d365fo generate map FmAddressMap \
--field Street:Name --field City:Name \
--map-to "FmVehicle:Street=AddrStreet,City" \
--map-to FmRental \
--install-to FleetManagement--field <name>:<edt>[:<label>] declares a map field; the EDT drives its concrete
type, resolved through the same index-backed lookup generate table uses, so a map
field and a table field on one EDT can never disagree. --map-to <table>[:pairs]
wires the map to a table; omit the pairs to connect every field by identical name.
Connecting a field the map does not declare is rejected rather than written.
Both write straight to disk — the metadata bridge's createObject only accepts
class/table/EDT/enum/form, so --install-to resolves the model folder and the
scaffold is written there (same as generate query).
# Scaffold a new role that references duties / privileges
d365fo generate role FmVehicleAdminRole \
--duty FmVehicleMaintainDuty --privilege FmVehicleReadPriv \
--label "@Fleet:VehicleAdminRole" --description "Full access to Fleet vehicles" \
--out src/MyModel/AxSecurityRole/FmVehicleAdminRole.xml
# Merge new references into an existing role (idempotent; writes .bak)
d365fo generate role --add-to src/MyModel/AxSecurityRole/FmVehicleAdminRole.xml \
--duty FmReportingDuty --privilege FmExportPriv--add-to validates the root element (AxSecurityRole), dedupes by Name (case-insensitive), and returns NoChange when every reference already exists.
# Minimal — one dataset, no columns specified (tablix shell only)
d365fo generate report FmVehicleReport \
--dp FmVehicleReportDP \
--tmp FmVehicleReportTmp \
--dataset FmVehicleDS \
--caption "@Fleet:VehicleReport" \
--out src/MyModel/AxReport/FmVehicleReport.xml
# With tablix column definitions — generates header row + data row in the tablix
d365fo generate report FmVehicleReport \
--dp FmVehicleReportDP --tmp FmVehicleReportTmp \
--field VIN --field Make --field Model --field Year \
--caption "@Fleet:VehicleReport" \
--out src/MyModel/AxReport/FmVehicleReport.xml
# With report parameters — auto-generates a DataContract class (FmVehicleReportDPContract.xml)
d365fo generate report FmVehicleReport \
--dp FmVehicleReportDP \
--field VIN --field Make --field Year \
--parameter FromDate:DateTime --parameter ToDate:DateTime --parameter Customer \
--out src/MyModel/AxReport/FmVehicleReport.xml
# Multi-dataset — primary dataset + additional via --extra-dataset
d365fo generate report FmFleetSummaryReport \
--dp FmFleetSummaryReportDP \
--field VehicleCount --field TotalMileage \
--extra-dataset FmFleetCostsDS:FmFleetCostsDP \
--extra-dataset FmFleetIncidentsDS:FmFleetIncidentsDP \
--parameter PeriodYear:Integer \
--out src/MyModel/AxReport/FmFleetSummaryReport.xmlThe command atomically writes two or three XML files: AxReport (datasets + auto-design + tablix), DP class extending SrsReportDataProviderBase, and (when --parameter is used) a DataContract class extending SrsReportDataContractBase. Output overrides: --out-dp, --out-contract.
# Scaffold event class + contract class
d365fo generate business-event CustCustomBusinessEvent \
--contract-name CustCustomBusinessEventContract \
--payload "custAccount:CustAccount" --payload "amount:AmountCur" \
--category "CustomerEvents" --primary-table CustTable \
--out c:/AOT/MyModel/AxClass/CustCustomBusinessEvent.xml \
--out-contract c:/AOT/MyModel/AxClass/CustCustomBusinessEventContract.xmlThe event class extends BusinessEventsBase and is decorated with [BusinessEvents(classStr(CustCustomBusinessEvent), classStr(CustCustomBusinessEventContract), "CustomerEvents", "...")]. The contract class implements BusinessEventsContract with parmXxx() accessors for each --payload entry.
After scaffolding, activate the event in System Administration > Business events catalog.
# Scaffold service XML + service class + service group
d365fo generate custom-service CustCustomService \
--class-name CustCustomServiceClass --group-name CustCustomServiceGroup \
--operation "processCustomer:CustAccount" \
--out c:/AOT/MyModel/AxService/CustCustomService.xml \
--out-class c:/AOT/MyModel/AxClass/CustCustomServiceClass.xml \
--out-group c:/AOT/MyModel/AxServiceGroup/CustCustomServiceGroup.xmlUse d365fo search service <name> to check for existing services before naming.
d365fo generate migration-script MigrateCustCustomData \
--source-table CustCustomTableOld --target-table CustCustomTable \
--batch-size 500 --mode Upsert \
--out c:/AOT/MyModel/AxClass/MigrateCustCustomData.xml--mode accepts Insert, Update, Upsert. Emits a Runnable class with doInsert/doUpdate (the one documented exception where do* methods are appropriate for migration logic) and configurable ttsbegin/ttscommit batching with progress logging.
d365fo generate runbase CustCustomReport \
--batch \
--dialog-param "fromDate:TransDate" --dialog-param "toDate:TransDate" \
--out c:/AOT/MyModel/AxClass/CustCustomReport.xml--batch adds canGoBatch() { return true; } and pack()/unpack() with a container member list. --dialog-param name:EdtType (repeatable) generates dialog field declarations and getFromDialog() logic. Use generate sysoperation for new code — generate runbase is provided for ISV teams maintaining older codebases.
d365fo generate security-policy CustCustomSecurityPolicy \
--constrained-table CustCustomTable --policy-query CustCustomPolicyQuery \
--operation Select --context-type RoleName --context-value CustCustomRole \
--out c:/AOT/MyModel/AxSecurityPolicy/CustCustomSecurityPolicy.xml--operation accepts All, Select, Insert, Update, Delete. --context-type accepts RoleName or ContextString. Use d365fo search security-policy <name> to audit existing XDS policies before adding new ones.
# Auto-detect object kind and rank extension strategies
d365fo suggest extension CustTable --output json
# Hint the kind explicitly when needed
d365fo suggest extension SalesFormLetterService --kind Class --output json
d365fo suggest extension SalesTable --kind Table --output json
d365fo suggest extension CustTable --kind Form --output jsonRanks extension strategies (CoC, EventHandler, Extension) by confidence based on what already exists in the codebase. Use before scaffolding.
# Analyse a full model folder (walks all *.xml recursively)
d365fo analyze completeness src/MyModel --output json
# Analyse a single AOT XML file
d365fo analyze completeness src/MyModel/AxSecurityRole/FmAdminRole.xml
# Skip slow label checks in CI (focus on structural refs only)
d365fo analyze completeness src/MyModel --skip-labels --output json
# Pipe into jq to count issues by code
d365fo analyze completeness src/MyModel --output json \
| jq '.data.issues | group_by(.code) | map({code: .[0].code, count: length})'Reports MISSING_DUTY, MISSING_PRIVILEGE, MISSING_EDT, MISSING_LABEL, PARSE_ERROR. Flags: --skip-labels, --skip-edts, --skip-security.
# Read
d365fo labels search "Customer invoice"
d365fo labels search "customer invoice" --fts # rank-sorted FTS5
d365fo get label @SYS12345 --language en-us
# Write \u2014 atomic, preserves comments, BOM UTF-8
d365fo label create NewKey "New value" --file path/Foo.en-us.label.txt
d365fo label create NewKey "Updated" --file path/Foo.en-us.label.txt --overwrite
d365fo label rename NewKey RenamedKey --file path/Foo.en-us.label.txt
d365fo label delete RenamedKey --file path/Foo.en-us.label.txtsearch label --fts requires FTS5 (falls back to LIKE if unavailable). Write commands keep a .bak of the previous file.
# Inspect the stack (most-recent-first)
d365fo journal list --output json
d365fo journal list --limit 5 --output json
# Preview before committing to an undo
d365fo undo --dry-run --output json
# Revert the last write, or the last N
d365fo undo --output json
d365fo undo --steps 3 --output json
# Delete an AOT object (journaled — undo-able like any other write)
d365fo delete --kind table --name OldTable --path C:\pkg\MyModel\MyModel\AxTable\OldTable.xml
d365fo delete --kind class --name OldClass --install-to MyModel # via the metadata bridgeEvery generate *, labels create\|rename\|delete, and delete write appends an entry to a
size-capped journal at <index-dir>/journal/. undo replays entries in reverse through the
same write path that produced them (disk or bridge), restoring the exact pre-image — a create
is removed, an update/delete is restored byte-for-byte. Stops at the first failure so older
entries are never skipped.
d365fo review diff --base HEADCompare two revs with --base main --head feature/my-branch. Rules shipped today:
FIELD_WITHOUT_EDT— table field without<ExtendedDataType>.FIELD_WITHOUT_LABEL— user-facing field without<Label>.HARDCODED_STRING— verbatim string literal in X++ source.DYNAMIC_QUERY— dynamicQueryconstruction (flag for security review).
These commands wrap the Microsoft tooling Visual Studio uses, so you can drive the IDE's workflow from a terminal, script, or CI pipeline.
d365fo build --project C:\AosService\PackagesLocalDirectory\MyModel\MyModel.rnrproj
d365fo sync --full
d365fo test run --suite MyModel.Tests
d365fo bp check --model MyModelEach parses the tool output and returns a structured JSON envelope (errors, warnings, elapsed time, tail of stdout). On non-Windows they return UNSUPPORTED_PLATFORM.
d365fo agent-prompt --out .prompts/d365fo.mdd365fo schema --full emits a machine-readable catalog of every command.
.\scripts\Install-D365FoCopilotSkills.ps1 -XppRepo "K:\D365FO\MyProject"Copilot auto-discovers .github/skills/d365fo-cli/ and loads the skill on demand — no extra configuration needed. It drives d365fo through its terminal tool.
Drop skills/anthropic/ into the project or ~/.claude/skills/. Each SKILL.md triggers via its applies_when front-matter.
Paste the output of d365fo agent-prompt into the session system prompt, or reference it from AGENTS.md.
Use the compact manifest when the agent needs to discover the CLI surface:
d365fo schemaUse the complete manifest only when needed:
d365fo schema --fullCLI-first shortcuts replace common MCP multi-call workflows:
d365fo search batch CustTable SalesTable CustAccount --output json
d365fo get object table CustTable --output json
d365fo find related coc CustTable --method validateWrite --output jsonStandalone JSON-RPC 2.0 server (protocol 2024-11-05) that shares the CLI's index. Config sample for Claude Desktop:
After dotnet publish src/D365FO.Mcp -c Release -r osx-arm64 you get a standalone d365fo-mcp binary you can drop on $PATH. The adapter exposes 20 consolidated, discriminator-based tools (search, get_object_info, get_method, extension_info, object_patterns, generate_object, security_info, labels, analyze, models, …) covering CLI parity, security & labels, heuristics, and aggregation — same index, same guardrails. See MIGRATION_FROM_MCP.md for the full tool list.
For latency-sensitive integrations, run the CLI as a daemon so the SQLite handle and read caches stay hot:
d365fo daemon start # named pipe / Unix socket + file watcher
d365fo daemon start --packages J:\AosService\PackagesLocalDirectory
d365fo daemon start --no-watch # disable auto index refresh
d365fo daemon start --watch-debounce 5000 # wait 5 s after last change before refresh
d365fo daemon status
d365fo daemon stopTransport: Windows named pipe \\\\.\\ pipe\\d365fo-cli; Unix socket at $XDG_RUNTIME_DIR/d365fo-cli.sock (fallback $TMPDIR). The frame format matches d365fo-mcp: one newline-terminated JSON-RPC request per connection, one response, close.
The daemon also starts a FileSystemWatcher over D365FO_PACKAGES_PATH (or --packages). When *.xml files change, it debounces (default 3 s) and automatically triggers an incremental index refresh for the affected model, emitting a JSON notification to stderr:
{ "event": "index_refreshed", "model": "Contoso" }Pass --no-watch to disable the watcher (e.g. read-only environments or network shares).
Every command is scriptable: exit codes are reliable, output is JSON by default in non-TTY, no interactive prompts.
- name: D365 review
run: |
d365fo index build
d365fo review diff --base origin/main --head HEAD --output json \
| jq -e '.data.violationCount == 0'- SETUP.md — install, configure, connect an AI agent.
- CAPABILITIES.md — full command reference and the built-in-editor-vs-
d365fodecision table. - ARCHITECTURE.md — index schema, lint rules, form pattern engine, the daemon.
- TROUBLESHOOTING.md — common failure modes and fixes.
{ "mcpServers": { "d365fo": { "command": "dotnet", "args": ["run", "--project", "/abs/path/to/src/D365FO.Mcp", "--no-build"], "env": { "D365FO_INDEX_DB": "/abs/path/d365fo-index.sqlite" } } } }