Koskamo

ALM XPP MCP

An MCP server for Microsoft Dynamics 365 Finance & Operations that provides 90+ AI tools to search 200K+ indexed X++ objects, trace security chains, generate code templates, and analyze Azure DevOps work items using real source code metadata.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

ALM XPP MCP logo, by ALM XPPCommunity listingStreamable HTTPNo sign-inEndpoint checked 2026-11-07Published by ALM XPP
You need:Create an accountRequest API TokenToken approval required for security
MCP endpoint (Streamable HTTP)
https://api.almxpp.com/mcp
Vendor documentation

What its tools do

104 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.

104 of 104 have a description · 104 of 104 declare behavior hints · 96 of 96 with parameters document them all

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Search 200K+ D365 objects like tables, classes, and forms
  • Trace security chains from Role to EntryPoint
  • Generate X++ code templates and CoC extensions
  • Analyze Azure DevOps work items and PRs

Source and maintenance

Community listingRegistry 1.5.1
Maintenance checked

Protocol and capabilities

Server
ALMXPPMCP 1.6.548
MCP protocol negotiated in check
2025-11-25
Tools
104 advertised
Resources
4 advertised
Resource templates
1 advertised
Prompts
4 advertised
Server-advertised capabilities
{
  "tools": {},
  "logging": {},
  "prompts": {},
  "resources": {}
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

<!-- ALMXPPMCP-v2026.08.10 | cache-stable: content changes only on KB version updates --> # ALMXPPMCP -- D365 F&O AI Agent (Enterprise Cloud) You have access to a D365 Finance & Operations X++ knowledge base (200K+ AOT objects, 1.3M+ indexed chunks, 25M+ cross-references) AND an Azure DevOps integration. Always prefer using a tool over answering from memory for anything D365-specific. ## [!] X++ IS NOT C#/JAVA -- MANDATORY ANTI-HALLUCINATION RULES Before writing or explaining ANY X++ code, internalize these non-negotiable differences (full catalogue: resource docs://xpp-hallucination-guardrails): - NO LINQ / NO async-await / NO Task<T> -- X++ uses select/while select/insert_recordset/update_recordset; long-running work uses SysOperation, never RunBase. - Table buffers (e.g. CustTable custTable;) are NEVER new'd for querying -- select populates them; new is only for inserting a brand-new record. - Every insert/update/delete needs explicit ttsbegin/ttscommit (no implicit transaction scope like C# using). - Types: str (not string), date/utcdatetime (not DateTime); var is valid X++ (type inferred), explicit types stay the house style. - Chain of Command: [ExtensionOf(classStr(Target))] final class X_Extension { ... next method(); ... } -- final + next() are MANDATORY (except [Replaceable]); the extension class does NOT inherit from the target. - User-facing strings are ALWAYS label references ("@LabelFile:LabelId"), never string literals in info()/warning()/error()/checkFailed(). - AOT metadata (tables/forms/enums/EDT/queries/security) is XML with a strict schema/element order -- never hand-author it as if it were X++ source; prefer this server's create_aot_object/write_aot_object/modify_aot_object/generate_xpp_template tools. - Security is ALWAYS Privilege -> Duty -> Role via entry points (menu items) -- never grant table/form access directly from a Role. -> For the FULL X++/grounding ruleset (naming, best practices, form patterns) BEFORE generating any non-trivial X++ code, call MCP prompt `xpp_system_instructions` (or `xpp_expert_guidelines`). -> For solution/project creation, build (xppc.exe), and deployment (PPAC/UDE vs LCS/CHE vs local -- do NOT confuse the two), read resource docs://xpp-solution-build-deploy. Real F&O development happens in Visual Studio, NOT VS Code. ## [!] DOMAIN SEPARATION -- CRITICAL ROUTING RULE There are TWO completely separate domains. NEVER mix them: ### AZURE DEVOPS DOMAIN -> tools: ado_query_workitems, ado_analyze_workitem, ado_post_comment, ado_create_task, ado_list_prs, ado_analyze_pr_impact, ado_post_pr_comment Keywords: Bug, Task, FDD, RDD, IDD, CR, User Story, Feature, ticket, sprint, backlog, Work Item, #1234, WI#, PR, Pull Request, DevOps ### D365 KNOWLEDGE BASE DOMAIN -> tools: search_labels, search_d365_code, get_object_details, find_references, find_extensions, generate_xpp_template, validate_best_practices, list_objects, ... Keywords: label, libellé, @SYS, @TRX, @FIN, @ACO, X++, AOT, table, class, form, enum, EDT, query, method, extension, CoC, error pattern, object ### RELEASE NOTE / VERSION DIFF DOMAIN -> tools: resolve_client_profile, save_client_profile, list_release_note_inputs, prepare_release_note_context, generate_release_note_document Keywords: release note, upgrade impact, regression, what changed between versions, version diff, note de version, régression potentielle [!] THIS SERVER holds the ACTUAL indexed diff between two SPECIFIC D365FO versions AND a client's ACTUAL uploaded custom code -- generic documentation/web-search tools (microsoft_docs_search, microsoft_docs_fetch, get_recent_azure_updates, or any other Microsoft Learn / Azure Updates MCP tool) do NOT have this data: they only know generic, PUBLIC release notes, never a specific client's real regression/opportunity picture. A Client Profile aggregates one client's full context (current + target version tags, ALL their custom models -- own extensions AND any attached ISV vendor models) keyed automatically by the caller's DEVOPS_ORG_URL/DEVOPS_PROJECT MCP headers -- once saved, v1/v2/customModelIds never need to be asked again for that client. Workflow (ALWAYS in this exact order, never skip step 1): 1) resolve_client_profile (auto-detects a saved profile for the CURRENT caller's ADO org/project -- no params) -> if found, its currentVersionTag/targetVersionTag/customModelIds can be omitted from step 3 (auto-filled); if not found, call list_release_note_inputs instead (discovers real version strings + custom model ids) and optionally save_client_profile to remember them for next time -> 2) (skip if profile found) list_release_note_inputs -> 3) prepare_release_note_context (v1, v2, customModelIds -- omit if a profile was found in step 1) -> 4) YOU analyze its 'objects' array per its 'instructions' field -> 5) generate_release_note_document with your findings JSON to get the downloadable Word + PowerPoint. ### ABSOLUTE ROUTING RULES - 'label', 'libellé', '@SYS', '@TRX', '@FIN', 'c\'est quoi le label', 'search_labels' -> ALWAYS -> search_labels. NEVER ado_query_workitems. - 'X++', 'AOT', 'table', 'class', 'form', 'method', 'extension' -> ALWAYS -> search_d365_code / get_object_details. NEVER ado_query_workitems. - 'Bug', 'Task', 'FDD', 'CR', 'ticket', '#1234', 'sprint' -> ALWAYS -> ado_* tools. NEVER search_d365_code. - 'release note', 'upgrade impact', 'what changed for this client between versions', 'regression risk' -> ALWAYS -> resolve_client_profile FIRST, then list_release_note_inputs if no profile, then prepare_release_note_context. NEVER microsoft_docs_search / microsoft_docs_fetch / get_recent_azure_updates / any generic documentation tool -- those only know public docs, not this server's real code diff. ## TOOL SELECTION -- Decision Tree ### Azure DevOps -> ado_query_workitems -- list/search Work Items (Bug, Task, FDD, CR, User Story...) -> ado_analyze_workitem -- deep analysis of a single Work Item (#id) -> ado_create_task -- create a development Task under a Work Item -> ado_list_prs -- list Pull Requests -> ado_analyze_pr_impact -- analyse PR code changes ### D365 KB -- Labels -> search_labels(query) -- find any D365 label by @LabelId (e.g. @SYS12345) or text (e.g. 'paiement'). ALL languages. Use for: 'c\'est quoi le label @SYS12345', 'traduire @FIN001', 'libellé de paiement' ### D365 KB -- Objects -> get_object_details(objectName) -- exact name known -> search_d365_code(query) -- name unknown, use natural language -> find_references(name) -- full scan, all objects that reference it -> find_extensions(baseObjectName) -- only CoC / extensions / event handlers -> find_related_objects(objectName) -- relations, data sources, form links ### D365 KB -- Code Generation -> generate_xpp_template(type, objectName) -- CoC, event handler, job, find method ### D365 KB -- Quality -> validate_best_practices(objectName) -- security, perf, transactions, error handling -> detect_performance_issues(objectName) -- N+1 queries, row-by-row, missing indexes -> validate_aot_pattern(objectName) -- naming, required elements, schema -> explain_code_complexity(objectName) -- cyclomatic complexity, control flow ### D365 KB -- Discovery -> list_objects(aotType, modelName) -- full index scan, ALL matching objects -> list_custom_model_objects(customModelPath?) -- disk scan of custom model folder ### D365 KB -- Security -> trace_security_chain(objectName) -- Role->Duty->Privilege->EntryPoint chain -> generate_security_governance_report() -- full report with license inference ### D365 KB -- Diagrams -> generate_diagram("er", objectName) -- entity-relationship diagram -> generate_diagram("flow", obj, method) -- execution flowchart -> generate_diagram("security", objectName) -- security chain diagram ### D365 KB -- Entities & Queries -> find_entity_for_table(tableName) -- finds AxDataEntityView exposing the table -> generate_data_entity(tableName, name) -- generate a new entity AOT XML -> generate_query(tableName, ...) -- X++ select + T-SQL with correct joins ### D365 KB -- Errors -> find_error_patterns(errorOrSymptom) -- known D365 error DB + label lookup ### D365 KB -- Refactoring & Docs -> suggest_refactoring(objectName) -- extract method, guard clauses, set-based -> find_similar_implementations(objectName) -- ranked by structural similarity -> generate_technical_doc(objectName) -- full markdown doc with fields/methods ### D365 KB -- Integration & Process -> generate_integration_code(objectName, type) -- OData C#, batch job, data import -> diff_model_versions(action, path) -- changelog between index snapshots -> map_business_process(processName) -- Order-to-Cash, Procure-to-Pay, etc. ### D365 KB -- Server Status -> healthcheck() -- status + loaded version + custom model path -> get_index_stats() -- chunk count by type and model ### Release Note / Version Diff (client-specific, NOT generic doc search) -> list_release_note_inputs() -- ALWAYS call first: discovers real version strings + custom model ids -> prepare_release_note_context(v1, v2, customModelIds, businessContext?) -- diff + cross-reference, no LLM call -> generate_release_note_document(v1, v2, customModelLabel, findingsJson, ...) -- renders Word + PowerPoint ## CUSTOM MODEL (local extension code on disk) - Tools reading disk require D365_CUSTOM_MODEL_PATH env var OR customModelPath param. - When NOT set: all KB tools work normally on indexed platform code. - When set: get_object_details, validate_best_practices, detect_performance_issues, explain_code_complexity automatically MERGE index + disk (disk takes priority). ## KEY RULES - ALWAYS call get_object_details before generating code -- use real metadata, not memory. - COST ROUTING: for relations / FK / DeleteActions / back-references ONLY, use get_relation_graph (O(1), ~6x lighter, no source) -- do NOT call get_object_details just for relations. Reserve get_object_details for field details, method bodies, or source code. For navigating a path between two objects use find_relation_path. - ALWAYS call find_extensions before modifying an object -- check existing customizations. - For broad discovery use list_objects (full scan), NOT search_d365_code (scoring-limited). - search_d365_code is for natural language / unknown names; get_object_details for exact names. - AOT types: AxClass, AxTable, AxForm, AxView, AxEnum, AxEdt, AxDataEntityView, AxSecurityPrivilege, AxSecurityDuty, AxSecurityRole, AxQuery, AxReport, Documentation.

Tools

104 advertised

validate_best_practices
Advertised by the server during the last successful anonymous check.
Advertised read-only

Run an in-house best-practice audit on any AOT object (custom or standard), from the indexed source -- NOT the Microsoft BP checker. Rules are this server's own: SEC (security chain), PERF (firstOnly, set-based, N+1), TXN (ttsbegin/ttscommit pairing), ERR (error handling), COC (next() vs super()), QUAL, DATA (EDT on fields), CONV (naming, ISV prefix) and CLOUD. It runs pre-compile and needs no D365 install, so it catches things while the code is being written -- but it does not replace xppbp.exe, whose rule set and monikers are different. For the authoritative Microsoft verdict run run_best_practices_check (xppc.exe -BestPractices, whole model) or run_best_practices_check_scoped (xppbp.exe, one object). Returns violation table: severity (Critical/Warning), rule ID, code snippet, fix instruction. For deep N+1 / row-by-row performance profiling use detect_performance_issues instead. [!] Auto-fixing Critical violations requires D365_CUSTOM_MODEL_PATH (custom code only).

validate_best_practices input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "methodName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: specific method to validate. Validates all methods if not provided."
    },
    "objectName": {
      "type": "string",
      "description": "Object name to validate, e.g. 'SalesTable', 'CustInvoiceJour'"
    },
    "minSeverity": {
      "type": "string",
      "default": "Warning",
      "description": "Minimum severity: 'Info', 'Warning', 'Critical' (default: 'Warning')"
    }
  }
}
validate_best_practices annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
find_extensions
Advertised by the server during the last successful anonymous check.
Advertised read-only

Find all extension objects (AxClassExtension, AxTableExtension, AxFormExtension, AxEnumExtension), CoC classes ([ExtensionOf]), and event handlers for a base object. Also queries Azure DevOps Code Search in real-time for custom extensions not yet in the KB. ALWAYS call before creating a new extension — avoid parallel customizations.

find_extensions input schema
{
  "type": "object",
  "required": [
    "baseObjectName"
  ],
  "properties": {
    "baseObjectName": {
      "type": "string",
      "description": "The base object name, e.g. 'SalesTable', 'SalesFormLetter', 'VendTable'"
    }
  }
}
find_extensions annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_create_bug_from_test
Advertised by the server during the last successful anonymous check.
Open-world

WHEN: a test case failed and the failure must become a Bug in Azure DevOps. Triggers: 'open a bug for this failed test', 'crée le bug depuis le test', 'file the defect'. Builds the Bug from the test case: its steps become the reproduction steps, `observed` is what actually happened, and it is linked (Related) to the test case and to the requirement under test. `observed` must be what was really seen: no personal data. SAFE BY DEFAULT: confirm=false only shows the Bug. Call again with confirm=true after the user agreed. An open Bug with the same title is not duplicated. Needs a PAT with Work Items (Read & write).

ado_create_bug_from_test input schema
{
  "type": "object",
  "required": [
    "testCaseId",
    "observed"
  ],
  "properties": {
    "title": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Bug title. Default: '[Test failed] ' and the test case title."
    },
    "confirm": {
      "type": "boolean",
      "default": false,
      "description": "false (default): preview only. true: create. Only after the user agreed."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Azure DevOps project. Falls back to DEVOPS_PROJECT."
    },
    "expected": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: what was expected, if the test case does not say it clearly."
    },
    "observed": {
      "type": "string",
      "description": "What was observed when it failed: the message, the wrong value, the screen."
    },
    "severity": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: 1-Critical, 2-High (default), 3-Medium, 4-Low."
    },
    "testCaseId": {
      "type": "integer",
      "description": "Id of the Test Case work item that failed."
    }
  }
}
ado_create_bug_from_test annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
dmf_import_file
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

Bulk-import a CSV file into a D365 F&O entity through the Data Management package REST API. Builds the package (Manifest + header + CSV) in memory, uploads it to Azure blob, then calls ImportFromPackage which AUTO-CREATES the data project from the manifest. Runs in batch; the tool polls until completion and returns the final status plus an error-keys file URL when rows fail. Provide either filePath (a .csv on disk) or inline csvContent. Resolve the entity name from the KB (find_entity_for_table) -- do not invent it.

dmf_import_file input schema
{
  "type": "object",
  "required": [
    "entityName",
    "legalEntity"
  ],
  "properties": {
    "execute": {
      "type": "boolean",
      "default": true,
      "description": "Execute the import after staging (true) or stage only (false). Default true."
    },
    "filePath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Path to a .csv file on disk. Provide this OR csvContent."
    },
    "overwrite": {
      "type": "boolean",
      "default": true,
      "description": "Overwrite an existing project definition with the same id. Default true."
    },
    "csvContent": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Inline CSV content (header row + data). Provide this OR filePath."
    },
    "entityName": {
      "type": "string",
      "description": "Target entity name as known to DMF (the entity, not the OData set), e.g. 'Customers V3'."
    },
    "legalEntity": {
      "type": "string",
      "description": "Legal entity / company id to import into, e.g. 'USMF'."
    },
    "definitionGroupId": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "DMF definition group (data project) id. Default: auto-generated from entity + timestamp."
    },
    "pollTimeoutSeconds": {
      "type": "integer",
      "default": 300,
      "description": "Max seconds to poll for completion before returning the executionId. Default 300."
    }
  }
}
dmf_import_file annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": true
}
search_d365_code
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: object name is unknown, partial, or you need to find by concept/keyword. Search the D365 F&O knowledge base for X++ code, tables, classes, forms, views, enums, EDTs, security objects using natural language or partial names. Returns ALL chunks (metadata, Declaration, methods) for the top-scoring objects so the LLM has complete context on the first call. Lower-scoring results return a short preview. No follow-up get_object_details call is needed for top results. NOT for listing all objects in a model -- use list_objects for that. NOT when the exact name is known -- use get_object_details for that. NEVER call search_d365_code twice in the same conversation turn. If one search did not find the object, answer from what you have -- do not repeat the search. When you need context on MORE THAN ONE concept simultaneously, use batch_search instead -- it runs all queries in parallel and is faster. NEVER call for ADO items (FDD, RDD, IDD, Bug, Task, PR, WorkItem, sprint, #1234) -- use ado_* tools instead.

search_d365_code input schema
{
  "type": "object",
  "required": [
    "query"
  ],
  "properties": {
    "topK": {
      "type": "integer",
      "default": 10,
      "description": "Maximum number of results to return (default: 10, max: 50)"
    },
    "query": {
      "type": "string",
      "description": "Natural language search query. Examples: 'vendor invoice approval', 'tables in ALM', 'SalesTable fields', 'security privileges for purchasing'"
    },
    "scope": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional routing override: 'auto' (default — session/custom first, standard fallback), 'custom' (session only — fails over to standard if nothing found), 'standard' (skip session entirely), 'both' (federated, no priority)."
    },
    "domain": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter results to a specific AOT type. Examples: 'AxTable', 'AxClass', 'AxForm', 'AxEnum'. Leave empty for all types."
    },
    "topObjects": {
      "type": "integer",
      "default": 5,
      "description": "Number of top-scoring objects whose ALL chunks are returned (default: 5, max: 20). Increase to broaden coverage, decrease for tighter focus."
    }
  }
}
search_d365_code annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
dmf_transform_excel
Advertised by the server during the last successful anonymous check.

Transform a multi-sheet Excel (.xlsx) into DMF-ready rows using a data-driven JSON mapping -- equivalent to FO_TransformExcelForDMF. NO FO credentials required (offline transform). Provide the workbook via ONE of: sourceUrl (Blob/SharePoint SAS URL or Graph downloadUrl), filePath (local .xlsx) or fileContentBase64 (inline upload, small files only). The mappingJson spec supports: sourceSheet, targetSheet, columnMappings {source->target}, conditionalValues [{sourceColumn,matches[],values{}}], staticValues{}, deduplicateOn[] and autoGeneratedFields (array OR object keyed by sheet, e.g. {"Products V2":["PRODUCTNUMBER"]}) which are removed so FO generates them. Returns CSV (default, feed to dmf_import_file) or JSON (feed to odata_upsert_rows). Set listSheetsOnly=true to just inspect the workbook's sheet names.

dmf_transform_excel input schema
{
  "type": "object",
  "properties": {
    "format": {
      "type": "string",
      "default": "csv",
      "description": "Output format: 'csv' (default) or 'json'."
    },
    "filePath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Local path to a .xlsx file. Provide one of sourceUrl/filePath/fileContentBase64."
    },
    "sheetName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Override the source sheet name. Empty = use mappingJson.sourceSheet, else the first sheet."
    },
    "sourceUrl": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "URL to the .xlsx (Blob/SharePoint SAS or Graph downloadUrl). Provide one of sourceUrl/filePath/fileContentBase64."
    },
    "outputPath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional file path to also write the full transformed result to."
    },
    "mappingJson": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Mapping spec JSON (columnMappings, conditionalValues, staticValues, deduplicateOn, autoGeneratedFields, sourceSheet). Optional when listSheetsOnly=true."
    },
    "listSheetsOnly": {
      "type": "boolean",
      "default": false,
      "description": "If true, only list the workbook's sheet names (diagnostic). mappingJson not required."
    },
    "fileContentBase64": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Base64-encoded .xlsx content (inline upload, small files). Provide one of sourceUrl/filePath/fileContentBase64."
    }
  }
}
dmf_transform_excel annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
generate_query
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer needs correct X++ select or T-SQL for D365 tables with proper joins. Triggers: 'X++ select', 'generate a query', 'SQL for', 'join with', 'how to query', 'générer une requête', 'write a select statement', 'select from', 'X++ query for', 'requête X++', 'écrire une select'. Generate both X++ select statements and equivalent T-SQL queries for D365 F&O tables. Uses real field names, relations, and indexes from the knowledge base to produce correct joins. Supports: field selection, multi-table joins (auto-detects relations), WHERE filters, ORDER BY, TOP/firstonly, cross-company. Also accepts natural language descriptions like 'find all open sales orders for customer 1001 with CustTable join'. [!] For multi-table joins, call find_related_objects (or get_relation_graph if the relation index is loaded) FIRST to get the correct FK relations -- this tool will then produce accurate join conditions. [!] The generated X++ is a template -- adapt it to your custom code context before using in production. Returns side-by-side X++ and SQL with explanations.

generate_query input schema
{
  "type": "object",
  "required": [
    "tableName"
  ],
  "properties": {
    "top": {
      "type": [
        "integer",
        "null"
      ],
      "default": null,
      "description": "Optional: limit rows (1 = firstonly, N = top N)"
    },
    "fields": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: specific fields to select (comma-separated). All fields if not specified."
    },
    "filters": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: WHERE filter expressions (comma-separated), e.g. 'CustAccount == 1001, SalesStatus == SalesStatus::Open'"
    },
    "orderBy": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: field to ORDER BY"
    },
    "tableName": {
      "type": "string",
      "description": "Primary table name, e.g. 'SalesTable', 'CustTable'"
    },
    "joinTables": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: tables to join (comma-separated), e.g. 'CustTable,SalesLine'"
    },
    "description": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: natural language description of the query. If provided, fields/joins/filters are auto-detected."
    },
    "crossCompany": {
      "type": "boolean",
      "default": false,
      "description": "Whether to add crosscompany clause (default: false)"
    }
  }
}
generate_query annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_wiki_delete_page
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

AZURE DEVOPS ONLY -- Delete a wiki page. This also deletes its sub-pages if any exist under it. Requires DEVOPS_ORG_URL + DEVOPS_PAT env vars (PAT needs Wiki: Read & Write scope).

ado_wiki_delete_page input schema
{
  "type": "object",
  "required": [
    "path"
  ],
  "properties": {
    "path": {
      "type": "string",
      "description": "Wiki page path to delete, e.g. '/Architecture/Old-Design'."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "wikiIdentifier": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Wiki name or id. Omit to auto-resolve to the project's only/first wiki (call ado_wiki_list if there are several)."
    }
  }
}
ado_wiki_delete_page annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": true
}
appinsights_diagnose_slowness
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

WHEN: user wants to understand/deduce why a D365 F&O environment feels slow, using the real Application Insights telemetry the environment already emits -- no trace file upload needed. Runs a set of canned KQL queries and assembles one combined report: 1. X++ exception hotspots (exceptions table) -- top types/messages by count. 2. Hot custom telemetry events (customEvents table) -- top event names by frequency, plus a duration breakdown for events that carry an ElapsedMilliseconds custom property (covers ALMMonitoring-instrumented FDDs, and any out-of-box signal using the same convention). 3. Slow web requests/dependencies (requests/dependencies tables) -- only rendered if the workspace actually has data there (uncommon for FnO's own AOS tier, but present for Commerce/Portal/custom web extensions sharing the same App Insights resource). Requires the connection to be configured first via appinsights_set_connection (or server env vars). Triggers: 'déduire et comprendre les lenteurs', 'why is my environment slow', 'diagnose slowness from App Insights', 'analyse la lenteur avec App Insights', 'performance issue live environment', 'slow environment telemetry'.

appinsights_diagnose_slowness input schema
{
  "type": "object",
  "properties": {
    "top": {
      "type": "integer",
      "default": 15,
      "description": "Max rows per section (1-50). Default 15."
    },
    "lookbackHours": {
      "type": "integer",
      "default": 24,
      "description": "How far back to analyze, in hours (1-720). Default 24."
    },
    "minDurationMs": {
      "type": "integer",
      "default": 2000,
      "description": "Minimum average duration (ms) for a custom event to be flagged as 'slow'. Default 2000."
    }
  }
}
appinsights_diagnose_slowness annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_wiki_list
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

AZURE DEVOPS ONLY -- List all wikis in a project (the project wiki plus any published code-as-wiki repos). Use this FIRST when the user doesn't know the wiki's exact name/id, or to confirm which wiki to target before reading/writing a page. Requires DEVOPS_ORG_URL + DEVOPS_PAT env vars.

ado_wiki_list input schema
{
  "type": "object",
  "properties": {
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    }
  }
}
ado_wiki_list annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
get_object_details
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: you know the EXACT object name. Triggers: user gives an exact name like 'SalesTable', 'CustTable', 'VendInvoiceJour', any PascalCase D365 object name. Get complete details: all fields, methods, relations, indexes, source code, and metadata. COST NOTE: this returns the FULL object and can be very large (100KB+ for big tables/forms like SalesLine). If you ONLY need relations / FK / DeleteActions / data sources / who-references, call get_relation_graph instead -- it is O(1) and roughly 6x lighter (no source code). Reserve get_object_details for when you genuinely need field details, method bodies, or source code. Also merges live disk source when a custom model path is configured (disk takes priority). Pass `methodName` to get the FULL body of a specific method -- without it, only signatures are returned. Calling twice -- first without methodName to get the full structure and method table, then again with a specific methodName for its full body -- is the CORRECT and INTENDED two-step pattern. Do NOT call a third time for the same object. DISAMBIGUATION: when a name exists as several AOT types (e.g. a Table AND a Form both named 'SalesLine'), this returns the PRIMARY type (Table/Class/Entity before the much heavier Form) and notes the others -- pass `aotType` (e.g. 'AxTable', 'AxForm', or shorthand 'table'/'form'/'class') to pick a specific one. This prevents dumping the heaviest object and truncating the rest. NOT for searching -- use search_d365_code when the name is uncertain. NOT for listing a model's objects -- use list_objects for that.

get_object_details input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "aotType": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: AOT type to disambiguate when several objects share the name, e.g. 'AxTable', 'AxForm', 'AxClass' (shorthand 'table'/'form'/'class'/'enum'/'edt'/'view'/'entity' also accepted). When omitted and the name is ambiguous, the primary type (Table/Class/Entity before Form) is returned with a note listing the others."
    },
    "methodName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: specific method name to return full body for, e.g. 'send', 'run', 'validate'. When provided, returns the complete method source instead of the signature table."
    },
    "objectName": {
      "type": "string",
      "description": "The exact object name, e.g. 'SalesTable', 'VendInvoiceApprovalConfig', 'ALMAlternativeItemsTable'"
    }
  }
}
get_object_details annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_query_workitems
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

AZURE DEVOPS ONLY -- Query Work Items (Bugs, Tasks, FDDs, User Stories, CRs) in Azure DevOps. [~] PRIORITY TRIGGER: use this tool when the user mentions 'FDD', 'RDD', 'IDD', 'CR', 'Task', 'Workitem', 'Work Item', 'Bug', 'User Story', 'Feature', 'Issue', 'ticket', 'sprint', 'backlog', 'DevOps', 'liste des tâches', 'show tasks', 'find bugs', '#1234', 'WI#'. NEVER use this tool for: D365 labels (@SYS/@TRX), X++ code, AOT objects, tables, classes, forms, enums, error messages, 'c\'est quoi le label', 'search_labels', 'libellé', 'label D365'. For labels -> use search_labels. For D365 code -> use search_d365_code or get_object_details. Shortcuts: 'bugs' (all active bugs), 'my bugs' (assigned to me), 'recent' (updated last 7 days), 'sprint' (current iteration). Or pass any WIQL SELECT statement or a free-text title search. Use '*' with filters only. Returns max 50 work items with ID, title, type, state, priority, area, assigned-to. Requires DEVOPS_ORG_URL + DEVOPS_PAT env vars.

ado_query_workitems input schema
{
  "type": "object",
  "required": [
    "query"
  ],
  "properties": {
    "tag": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by tag (partial match), e.g. 'D365' or 'hotfix'."
    },
    "query": {
      "type": "string",
      "description": "WIQL query, shortcut ('bugs', 'my bugs', 'recent', 'sprint'), title search, or '*' to use filters only."
    },
    "state": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by state, e.g. 'Active', 'New', 'Resolved', 'Closed'."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "areaPath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by area path (exact or under), e.g. 'MyProject\\SER'."
    },
    "priority": {
      "type": [
        "integer",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by priority level: 1 (Critical), 2 (High), 3 (Medium), 4 (Low)."
    },
    "assignedTo": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by assignee display name or email (partial match), e.g. 'Alim' or 'alim@contoso.com'."
    },
    "whereClause": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: extra WHERE condition(s) for custom fields, appended with AND. Use the Field Reference Name, e.g. \"[Custom.Extensionlifecyclestatus]='010. Registered'\". Multiple conditions: separate with ' AND '."
    },
    "changedSince": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: only return items changed since this date, e.g. '2025-01-01' or '7' (days ago)."
    },
    "workItemType": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by work item type, e.g. 'Bug', 'Task', 'User Story', 'Feature'."
    },
    "iterationPath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by iteration/sprint path, e.g. 'MyProject\\Sprint 5'. Use 'current' for @CurrentIteration."
    }
  }
}
ado_query_workitems annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
dmf_apply_entity_filter
Advertised by the server during the last successful anonymous check.
Open-world

Apply a single-field value filter to ONE entity row of an existing DMF project, so the export only contains the matching records. The kernel builds the packed query (custom 'GenerateQueryData' action), the container envelope is stripped on the QueryRun.pack() signature, and the inner blob is PATCHed onto DataManagementDefinitionGroupDetails.QueryData (the exact format the standard table DMFDefinitionGroupEntity stores). Works for ANY entity and ANY field, but only a single field = single value per call (call once per entity row). Wildcards are passed through (e.g. value '1001*'). NOTE: filtering a parent (e.g. 'Vendors V2' on VendorAccountNumber) does NOT auto-filter child rows -- apply the same filter to related rows (e.g. 'Vendor bank accounts', 'Vendor postal addresses') for a single-record export. Requires the custom 'GenerateQueryData' action in the environment.

dmf_apply_entity_filter input schema
{
  "type": "object",
  "required": [
    "projectName",
    "entityRowName",
    "aotEntityName",
    "fieldName",
    "filterValue"
  ],
  "properties": {
    "fieldName": {
      "type": "string",
      "description": "Entity field to filter on, e.g. 'VendorAccountNumber'."
    },
    "filterValue": {
      "type": "string",
      "description": "Value or wildcard to match, e.g. '1001' or '1001*'."
    },
    "projectName": {
      "type": "string",
      "description": "Existing DMF project (definitionGroupId) in FO."
    },
    "aotEntityName": {
      "type": "string",
      "description": "AOT entity name the kernel resolves, e.g. 'VendVendorV2Entity'."
    },
    "entityRowName": {
      "type": "string",
      "description": "Entity row label as shown on the project line, e.g. 'Vendors V2'."
    }
  }
}
dmf_apply_entity_filter annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_update_workitem
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

Update one or more fields on an existing Azure DevOps Work Item. Can update any standard field (System.Description, System.Title, System.State, System.AssignedTo, ...) or custom field (Custom.Extensionlifecyclestatus, ...) using its Field Reference Name. Triggers: 'mets à jour la description du workitem', 'change le state', 'update field', 'post ça en description'. Always show the user what will be changed and ask for confirmation before calling. Requires DEVOPS_PAT with Work Items: Write permission.

ado_update_workitem input schema
{
  "type": "object",
  "required": [
    "workItemId",
    "fieldsJson"
  ],
  "properties": {
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name."
    },
    "fieldsJson": {
      "type": "string",
      "description": "JSON object mapping Field Reference Names to values. Examples: {\"System.Description\":\"<p>New desc</p>\"} or {\"System.State\":\"Active\",\"Custom.MyField\":\"value\"}. Use HTML for Description/ReproSteps. Standard field refs: System.Title, System.State, System.AssignedTo (email), System.AreaPath, System.Tags, System.Description, Microsoft.VSTS.Common.Priority, Microsoft.VSTS.TCM.ReproSteps."
    },
    "workItemId": {
      "type": "integer",
      "description": "Work item ID to update."
    }
  }
}
ado_update_workitem annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": true
}
find_references
Advertised by the server during the last successful anonymous check.
Advertised read-only

Full index scan for all usages of an object, method, field, or label ID. Use for impact analysis before changing or deleting an object. EXPENSIVE — O(1M+ chunks). Prefer find_callers when XRef index is loaded (O(1)). Label IDs: automatically searches both `@SYS124480` and `@SYS:124480` forms. NOT for extensions only — use find_extensions for CoC/event handlers.

find_references input schema
{
  "type": "object",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "type": "string",
      "description": "The object or method name to find references to, e.g. 'SalesTable', 'validateWrite'"
    },
    "maxResults": {
      "type": "integer",
      "default": 500,
      "description": "Maximum referencing objects to return (default: 500, max: 2000)"
    },
    "locationsPerObject": {
      "type": "integer",
      "default": 6,
      "description": "Maximum number of call sites (methods/fields) to display per referencing object (default: 6, max: 20). Increase to see more usage locations."
    }
  }
}
find_references annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_gap_fit_analysis
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

WHEN: user asks whether a D365 requirement is standard, needs config, needs an extension, or is a full gap. Also triggered by gap analysis or fit/gap classification of a Work Item. GAP / FIT CLASSIFIER -- Analyse a D365 F&O requirement (from an ADO Work Item OR plain text) and classify it as one of four verdicts: [OK] Standard Fit -- D365 covers this out-of-the-box, no dev needed [gear] Config Fit -- D365 has it but requires parameter / profile setup Extension Fit -- Standard process exists; a CoC/event-handler is enough [X] Gap -- No standard coverage; custom development required For each requirement block the tool returns: -- Detected D365 domain (Settlement, PaymentJournal, DataImport, ...) -- Standard objects found in KB and their process step -- Existing extensions in the custom model (if D365_CUSTOM_MODEL_PATH is set) -- Effort estimate (hours) and a one-paragraph reasoning Triggers: 'analyse the requirement', 'is this a gap or fit', 'gap analysis WI #N', 'standard or custom for WI #N', 'does D365 cover this'. [~] When a WI has already been analysed by `ado_analyze_workitem` in the same turn, pass the requirement text directly via `requirementText` -- do NOT re-fetch with `workItemId`.

ado_gap_fit_analysis input schema
{
  "type": "object",
  "properties": {
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "workItemId": {
      "type": [
        "integer",
        "null"
      ],
      "default": null,
      "description": "Optional: Work Item ID to fetch the requirement from ADO (e.g. 6587). Leave empty to use requirementText."
    },
    "requirements": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: comma-separated list of requirement sentences to analyse individually (overrides WI + requirementText)."
    },
    "requirementText": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: raw requirement text to analyse directly (when no WI ID is provided)."
    }
  }
}
ado_gap_fit_analysis annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
batch_search
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: you need context on multiple D365 objects or concepts simultaneously -- runs all queries in parallel. Use INSTEAD of multiple sequential search_d365_code calls -- each line becomes one parallel search. Maximum 6 queries per call. Results are equivalent to search_d365_code but returned together. When batch_search returns results, all matching objects are FULLY loaded (all chunks). Do NOT follow up with get_object_details on the same objects -- the complete source is already included. Triggers: 'find all of these', 'look up multiple', 'cherche plusieurs', 'SalesTable AND VendTable', 'several objects at once', 'lookup X and Y and Z', 'plusieurs objets en même temps', 'context on all of these'.

batch_search input schema
{
  "type": "object",
  "required": [
    "queries"
  ],
  "properties": {
    "topK": {
      "type": "integer",
      "default": 5,
      "description": "Results per query (default: 5, max: 15)"
    },
    "queries": {
      "type": "string",
      "description": "Newline-separated search queries (max 6). Example: \"SalesTable\\nVendTable\\nvendor invoice posting\""
    },
    "topObjects": {
      "type": "integer",
      "default": 3,
      "description": "Full-chunk objects per query (default: 3, max: 8)"
    }
  }
}
batch_search annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_read_attachment
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

AZURE DEVOPS ONLY -- Reads the ACTUAL CONTENT of a file attached to a work item (Excel spreadsheet, Word document, text/CSV/JSON/XML file, or image). WHEN: a work item (FDD/RDD/CR/Bug/Task/User Story) has an Excel/Word attachment with requirements, field mappings, mockups, or specs that need to be read to understand the ask. Triggers: 'read the attachment', 'open the excel file on the work item', 'what does the attached document say', 'lis le fichier joint', 'ouvre l'excel du ticket'. Call ado_analyze_workitem first (or ado_query_workitems) to discover attachment file names if you don't already know the exact fileName. Supported: .xlsx/.xlsm (returns sheet names + a markdown table of the requested/first sheet), .docx (returns extracted markdown text + tables), .txt/.csv/.json/.xml/.md/.log (returned as-is), images (.png/.jpg/.jpeg/.gif/.bmp/.webp, returned as a base64 data URI for visual analysis, max 4 MB). Other binary formats (PDF, .pptx, .zip, etc.) are NOT parsed -- returns metadata + a manual download link instead. Max attachment size read: 25 MB. Requires DEVOPS_ORG_URL + DEVOPS_PAT env vars.

ado_read_attachment input schema
{
  "type": "object",
  "required": [
    "workItemId",
    "fileName"
  ],
  "properties": {
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "fileName": {
      "type": "string",
      "description": "Exact attachment file name as shown by ado_analyze_workitem, e.g. 'Field Mapping.xlsx'. Case-insensitive."
    },
    "sheetName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: sheet name for .xlsx/.xlsm attachments. Defaults to the first sheet."
    },
    "workItemId": {
      "type": "integer",
      "description": "Work item ID, e.g. 1234"
    }
  }
}
ado_read_attachment annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
erp_read_session
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

WHEN: to READ from the ERP through form tools, where several calls must share ONE ERP session: open a form, filter its grid, read its values. Each separate erp_read / erp_call starts a new ERP session, so a form opened by one call is gone at the next; this tool runs the steps in order in a single session. Every step must be a read-only tool (opening, filtering, selecting, reading); it refuses the whole call, before running anything, if one step could change data. It stops at the first step that fails. stepsJson: [{"tool":"form_open_menu_item","arguments":{...}}, ...], at most 15 steps. Close the forms you open as the last step.

erp_read_session input schema
{
  "type": "object",
  "required": [
    "instance",
    "stepsJson"
  ],
  "properties": {
    "instance": {
      "type": "string",
      "description": "Instance alias from erp_list_instances."
    },
    "stepsJson": {
      "type": "string",
      "description": "JSON array of steps: [{\"tool\":\"<name from erp_list_tools>\",\"arguments\":{...}}]."
    }
  }
}
erp_read_session annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
trace_field_lineage
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: you need to understand the full data lifecycle of a D365 F&O table field: who writes it, who reads it, which forms display it, which reports use it, and which tables have FK relationships to its parent table. Triggers: 'where is field X set', 'qui écrit ce champ', 'data lineage', 'GDPR field audit', 'origin of field', 'what touches this field'. Requires XRef index for writer/reader analysis. Relation graph for forms and FKs. Examples: trace_field_lineage('CustTable','CreditMax') or trace_field_lineage('LedgerJournalTrans','AmountCurDebit').

trace_field_lineage input schema
{
  "type": "object",
  "required": [
    "tableName",
    "fieldName"
  ],
  "properties": {
    "fieldName": {
      "type": "string",
      "description": "Field or property name, e.g. 'CreditMax'."
    },
    "tableName": {
      "type": "string",
      "description": "Table or class name, e.g. 'CustTable'."
    },
    "maxPerCategory": {
      "type": "integer",
      "default": 15,
      "description": "Max results per category (default 15)."
    }
  }
}
trace_field_lineage annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
plan_and_execute
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

CHAIN-OF-TOOLS PLANNER -- Execute a multi-step JSON plan in ONE call by chaining tools. Pass an EXPLICIT JSON array of steps (NL parsing disabled to prevent ambiguity). Maximum 5 steps per plan. The chain stops on any critical failure (ado_analyze_workitem, get_object_details). ## Required Format (JSON array) `[{"tool":"get_object_details","args":{"objectName":"SalesTable"}},{"tool":"validate_best_practices","args":{"objectName":"SalesTable"}}]` ## When to use - You already know the exact sequence of 2-5 tool calls needed - The output of step N is independent of step N+1 (or matches the carried context: analysis_output) - You want SSE progress events between steps ## When NOT to use - Single tool call (use the tool directly) - Plan depends on the user's intermediate decision (call tools one at a time) - More than 5 steps (split into separate calls) ## Cost: $0 -- pure in-process execution.

plan_and_execute input schema
{
  "type": "object",
  "required": [
    "goal"
  ],
  "properties": {
    "goal": {
      "type": "string",
      "description": "Natural language goal OR a JSON array of explicit steps. Examples: 'analyse WI #6587 and create a task', 'check best practices for ALMMyClass'."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "assignTo": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: assignee email for task creation step, e.g. 'dev@company.com'."
    },
    "objectName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: object name to use when goal references a D365 object (overrides name parsed from goal)."
    },
    "workItemId": {
      "type": [
        "integer",
        "null"
      ],
      "default": null,
      "description": "Optional: work item ID to use when goal references 'WI #N' (overrides ID parsed from goal text)."
    },
    "description": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: additional description text to pass to task creation step."
    }
  }
}
plan_and_execute annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": true
}
find_similar_implementations
Advertised by the server during the last successful anonymous check.
Advertised read-only

Find D365 objects structurally similar to the one you supply (same kind, similar method set, similar field set). Useful when you need a working reference implementation to copy from.

find_similar_implementations input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "maxResults": {
      "type": "integer",
      "default": 15,
      "description": "Maximum results (default: 15, max: 30)"
    },
    "objectName": {
      "type": "string",
      "description": "Object name to find similar objects for, e.g. 'ALMERSftpConnectionTable', 'SalesTable'"
    },
    "filterAotType": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter to a specific AOT type, e.g. 'AxTable', 'AxClass'. If not set, uses the same type as the source object."
    }
  }
}
find_similar_implementations annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
recommend_extension_strategy
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer about to customise a standard D365 object asks 'should I use AxTableExtension, Chain of Command, EventHandler, or Delegate?'. Triggers: 'how to extend', 'best way to customise', 'extension strategy for', 'CoC ou event handler', 'comment etendre X'. Returns a ranked recommendation based on the target AOT type and the intent, with citations to the Microsoft Learn extensibility guide. Cloud-safe: pure KB lookup, no writes.

recommend_extension_strategy input schema
{
  "type": "object",
  "required": [
    "objectName",
    "intent"
  ],
  "properties": {
    "intent": {
      "type": "string",
      "description": "Plain-language goal of the customisation. Examples: 'add a status field', 'change validateWrite logic', 'react to record insert', 'replace the posting logic', 'override the find method', 'extend the enum'."
    },
    "objectName": {
      "type": "string",
      "description": "Target standard D365 object name, e.g. 'SalesTable', 'CustTable', 'SalesLineType', 'PurchFormLetter_Confirmation'."
    }
  }
}
recommend_extension_strategy annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
diff_model_versions
Advertised by the server during the last successful anonymous check.
Advertised read-only

Compare two D365 F&O KB snapshots to produce a changelog (added/removed/modified objects). Use action 'save_baseline' once, then 'compare' between two snapshots.

diff_model_versions input schema
{
  "type": "object",
  "required": [
    "action",
    "path1"
  ],
  "properties": {
    "path1": {
      "type": "string",
      "description": "For 'compare': path to baseline cache file. For 'compare_caches': path to first cache. For 'save_baseline': output path for the baseline file."
    },
    "action": {
      "type": "string",
      "description": "Action: 'compare' (current vs baseline), 'compare_caches' (two cache files), or 'save_baseline' (save current state)"
    },
    "filterModel": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter to a specific model name"
    },
    "path2OrLabel": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: For 'compare_caches': path to second cache file. For 'compare'/'save_baseline': label for the baseline."
    }
  }
}
diff_model_versions annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
find_related_objects
Advertised by the server during the last successful anonymous check.
Advertised read-only

Returns ALL FK/DeleteAction/DataSource relations (outgoing) AND back-references (incoming). Call BEFORE generating multi-object code to understand the full dependency graph. When the relation index is loaded, delegates to get_relation_graph (O(1)) internally — do NOT call both tools for the same object.

find_related_objects input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "objectName": {
      "type": "string",
      "description": "The object name to find relationships for, e.g. 'SalesTable', 'VendInvoiceJour'"
    }
  }
}
find_related_objects annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_review_xpp_pr
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

WHEN: reviewing a PR that modifies X++ code or AOT objects and you need D365-specific insights. Returns a structured code review for each changed object: blast radius (who calls it), best-practice violations found in the PR's own source (fetched from the source branch; falls back to the indexed version and says so when the file cannot be fetched), and impact severity. BP findings come from this server's deterministic rule set, not from xppbp.exe -- a PR is uncompiled, so Microsoft's checker cannot run on it. Use find_error_patterns for Microsoft rule text. Triggers: 'review this PR', 'code review D365', 'analyse les changements', 'impact de la PR', 'what could break', 'blast radius of these changes', 'reverifie le code'. Requires DEVOPS_ORG_URL + DEVOPS_PAT (Code: Read scope) AND XRef index for impact analysis. Combine with ado_post_pr_comment to post findings as inline review comments.

ado_review_xpp_pr input schema
{
  "type": "object",
  "required": [
    "prId"
  ],
  "properties": {
    "prId": {
      "type": "integer",
      "description": "Pull Request ID to review."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "repositoryId": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Git repository name or ID."
    },
    "maxDeepAnalysis": {
      "type": "integer",
      "default": 8,
      "description": "Max AOT objects to deeply analyse (1-20, default 8). Objects beyond this limit are listed but not deeply analysed."
    }
  }
}
ado_review_xpp_pr annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
lesson_save
Advertised by the server during the last successful anonymous check.

WHEN: a problem has just been solved, or a convention or procedure was agreed, and it should not need explaining again. Triggers: 'keep this', 'retiens ça', 'save this lesson', 'note this for next time', 'capitalise'. Propose it to the user first and save only with their agreement. Write it so a stranger could apply it: the problem as it showed up (the symptom and message), the cause, the solution step by step, and the D365 objects involved. The lesson is saved for the caller's client as UNVALIDATED; an administrator reviews it before it is trusted. Never put secrets, passwords, tokens or personal data in a lesson.

lesson_save input schema
{
  "type": "object",
  "required": [
    "title",
    "problem",
    "solution"
  ],
  "properties": {
    "kind": {
      "type": "string",
      "default": "lesson",
      "description": "'lesson' (default), 'convention' (how this client does things) or 'procedure' (a repeatable how-to)."
    },
    "tags": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: comma-separated keywords."
    },
    "cause": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Why it happened, when known."
    },
    "title": {
      "type": "string",
      "description": "Short title that says what it is about, e.g. 'Sales invoice posting fails when the tax group is empty'."
    },
    "objects": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: comma-separated D365 objects involved."
    },
    "problem": {
      "type": "string",
      "description": "The problem as it appeared: symptom, error message, where."
    },
    "solution": {
      "type": "string",
      "description": "What fixed it, in steps."
    },
    "sourceRef": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: where it came from, e.g. 'WI 6587' or 'PR 142'."
    }
  }
}
lesson_save annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": false,
  "destructiveHint": false
}
dmf_create_data_project
Advertised by the server during the last successful anonymous check.
Open-world

Create (or extend) a DMF data project -- EXPORT or IMPORT -- entirely through standard public OData entities, with NO X++ customization required. It POSTs the header to DataManagementDefinitionGroups and one row per entity to DataManagementDefinitionGroupDetails with AutoGenerateMapping=Yes, so FO generates each entity's source<->staging mapping automatically. Idempotent: an existing project is reused and entities already present are skipped. For EXPORT, the project can then be run with dmf_export_package. Provide an EXISTING DMF 'source data format' name for sourceName (e.g. a comma-delimited format).

dmf_create_data_project input schema
{
  "type": "object",
  "required": [
    "projectName",
    "entities",
    "sourceName"
  ],
  "properties": {
    "entities": {
      "type": "string",
      "description": "Comma-separated DMF entity names, e.g. 'Customers V3,Released products V2'."
    },
    "sourceName": {
      "type": "string",
      "description": "Existing DMF source data format name that defines the file format."
    },
    "projectName": {
      "type": "string",
      "description": "Name of the data project (definitionGroupId) to create or extend."
    },
    "operationType": {
      "type": "string",
      "default": "Export",
      "description": "Operation type: 'Export' (default) or 'Import'."
    }
  }
}
dmf_create_data_project annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
find_entity_for_table
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer needs to integrate via OData and wants to find the entity name for a given table. Also generates a new entity template when none exists and generateIfMissing=true. Triggers: 'which entity exposes', 'OData entity for', 'find entity for', 'quelle entité expose', 'DMF entity for', 'create data entity', 'expose via OData', 'generate entity', 'entité de données'. Find D365 F&O data entities that expose a given table for OData/DMF integrations. Answers: 'Which entity exposes SalesTable for OData?' Scans all indexed AxDataEntityView objects to find entities with matching data sources. Returns entity name, public entity name (for OData URL), IsPublic status, key fields, and all data sources. Essential for integration development. Set generateIfMissing=true to auto-generate an AxDataEntityView XML template when no public entity is found.

find_entity_for_table input schema
{
  "type": "object",
  "required": [
    "tableName"
  ],
  "properties": {
    "tableName": {
      "type": "string",
      "description": "Table name to find entities for, e.g. 'SalesTable', 'CustTable', 'VendInvoiceJour'"
    },
    "maxResults": {
      "type": "integer",
      "default": 15,
      "description": "Maximum results (default: 15, max: 30)"
    },
    "generateIfMissing": {
      "type": "boolean",
      "default": false,
      "description": "When true and no public entity is found, generates a new AxDataEntityView XML template for the table. Default: false."
    }
  }
}
find_entity_for_table annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
summarize_for_stakeholder
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: you have a large technical tool output (get_object_details, validate_best_practices, ado_analyze_workitem, detect_performance_issues...) and need it reframed for a non-technical audience. Calls the local Ollama instance (OLLAMA_HOST env var, default localhost:11434) to produce the summary. Model is configurable via ALMXPP_SUMMARIZE_MODEL (default: llama3.2).

summarize_for_stakeholder input schema
{
  "type": "object",
  "required": [
    "text"
  ],
  "properties": {
    "text": {
      "type": "string",
      "description": "The technical text to summarize/explain -- typically the raw output of another tool call."
    },
    "audience": {
      "type": "string",
      "default": "business-analyst",
      "description": "Target audience: 'executive', 'business-analyst', or 'developer'. Default 'business-analyst'."
    }
  }
}
summarize_for_stakeholder annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_create_regression_suite
Advertised by the server during the last successful anonymous check.
Open-world

WHEN: the test cases of some touched objects should be grouped in one suite of an existing test plan, to replay them. Triggers: 'create the regression suite', 'suite de non-régression pour la PR N', 'group the tests of these objects'. Creates a query-based (dynamic) suite that gathers by itself every test case tagged obj:<object>. SAFE BY DEFAULT: confirm=false only shows the suite and how many cases it would gather now. Call again with confirm=true after the user agreed. A suite with the same name in the plan is reused. Needs a PAT with Test Management (Read & write) and the Basic + Test Plans access level.

ado_create_regression_suite input schema
{
  "type": "object",
  "required": [
    "planId",
    "objects"
  ],
  "properties": {
    "name": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Suite name. Default: 'Regression - ' and the first objects."
    },
    "planId": {
      "type": "integer",
      "description": "Id of the test plan that will hold the suite."
    },
    "confirm": {
      "type": "boolean",
      "default": false,
      "description": "false (default): preview only. true: create. Only after the user agreed."
    },
    "objects": {
      "type": "string",
      "description": "Comma-separated D365 objects whose test cases the suite gathers."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Azure DevOps project. Falls back to DEVOPS_PROJECT."
    }
  }
}
ado_create_regression_suite annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
suggest_refactoring
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer wants to improve code quality before a PR merge or code review. Triggers: 'refactor', 'clean up', 'simplify', 'too long method', 'nested ifs', 'code smells', 'améliorer le code'. Suggest concrete refactoring actions for YOUR custom D365 F&O X++ code. [!] Only runs on custom/extension code (D365_CUSTOM_MODEL_PATH). Refactoring standard Microsoft code is not actionable. Analyzes: long methods (extract method), deep nesting (guard clauses), row-by-row operations (set-based), large switch statements (strategy pattern), hardcoded strings (constants), unprotected CLR calls (error handling), wide transactions (narrow scope). Returns before/after code examples.

suggest_refactoring input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "methodName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: specific method to analyze."
    },
    "objectName": {
      "type": "string",
      "description": "Object name to analyze, e.g. 'ALMMyClass', 'ALMMyTable'"
    }
  }
}
suggest_refactoring annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
resync_devops_index
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

WHEN: the user wants to force a full re-download and re-index of their Azure DevOps custom model. Triggers: 'resync', 'reindex', 'force sync', 'rebuild index', 'my model is stale', 'update index', 'mon index est vieux', 'rafraîchir l\'index', 'relancer l\'indexation'. Useful when the server's built-in PAT does not have access to the target DevOps organisation (e.g. a different Azure DevOps org or tenant). Pass a `pat` parameter to override the server PAT for this resync. The eviction + download runs in the background; returns status immediately. After calling, wait ~60 s then call `healthcheck` or any search tool to confirm the index is ready.

resync_devops_index input schema
{
  "type": "object",
  "properties": {
    "pat": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Personal Access Token with Code (Read) permission for the target Azure DevOps org. Provide this when the server's built-in PAT lacks access (e.g. a different tenant, cross-org). Leave blank to reuse the session PAT or server env-var default."
    }
  }
}
resync_devops_index annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": true
}
erp_list_tools
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

WHEN: after erp_list_instances, to discover which tools the ERP MCP server of ONE instance exposes (name, description, input schema, read-only hint) before calling erp_call. Results are cached for 5 minutes.

erp_list_tools input schema
{
  "type": "object",
  "required": [
    "instance"
  ],
  "properties": {
    "instance": {
      "type": "string",
      "description": "Instance alias from erp_list_instances, e.g. \"uat\"."
    }
  }
}
erp_list_tools annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_wiki_get_page
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

AZURE DEVOPS ONLY -- Read the content of a wiki page (and its immediate sub-page paths). path uses wiki page-path syntax, e.g. '/Home' or '/Architecture/Deployment'. Requires DEVOPS_ORG_URL + DEVOPS_PAT env vars.

ado_wiki_get_page input schema
{
  "type": "object",
  "required": [
    "path"
  ],
  "properties": {
    "path": {
      "type": "string",
      "description": "Wiki page path, e.g. '/Home' or '/Architecture/Deployment'."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "wikiIdentifier": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Wiki name or id. Omit to auto-resolve to the project's only/first wiki (call ado_wiki_list if there are several)."
    },
    "includeSubPages": {
      "type": "boolean",
      "default": false,
      "description": "Also list immediate sub-page paths under this page."
    }
  }
}
ado_wiki_get_page annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
dmf
Advertised by the server during the last successful anonymous check.
Open-world

Consolidated Data Management Framework tool — one entry point for all 5 DMF operations, selected via `action`. Each action forwards to the exact same implementation as its dedicated tool (dmf_import_file / dmf_create_data_project / dmf_apply_entity_filter / dmf_export_package / dmf_get_job_status) — identical behavior and validation, just one tool schema instead of five. The dedicated tools remain available unchanged. action="import" (= dmf_import_file): entityName, legalEntity, filePath OR csvContent, definitionGroupId?, execute?, overwrite?, pollTimeoutSeconds? action="create_project" (= dmf_create_data_project): projectName, entities, sourceName, operationType? ('Export' default or 'Import') action="apply_filter" (= dmf_apply_entity_filter): projectName, entityRowName, aotEntityName, fieldName, filterValue action="export" (= dmf_export_package): definitionGroupId, legalEntity, packageName?, reExecute?, pollTimeoutSeconds? action="status" (= dmf_get_job_status): executionId

dmf input schema
{
  "type": "object",
  "required": [
    "action"
  ],
  "properties": {
    "action": {
      "type": "string",
      "description": "import | create_project | apply_filter | export | status"
    },
    "execute": {
      "type": "boolean",
      "default": true,
      "description": "import: execute after staging. Default true."
    },
    "entities": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "create_project: comma-separated DMF entity names."
    },
    "filePath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "import: path to a .csv file on disk (or use csvContent)."
    },
    "fieldName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "apply_filter: field to filter on, e.g. 'VendorAccountNumber'."
    },
    "overwrite": {
      "type": "boolean",
      "default": true,
      "description": "import: overwrite existing project definition. Default true."
    },
    "reExecute": {
      "type": "boolean",
      "default": true,
      "description": "export: re-execute the project. Default true."
    },
    "csvContent": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "import: inline CSV content (or use filePath)."
    },
    "entityName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "import: target DMF entity name, e.g. 'Customers V3'."
    },
    "sourceName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "create_project: existing DMF source data format name."
    },
    "executionId": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "status: the executionId returned by an import or export action."
    },
    "filterValue": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "apply_filter: value or wildcard to match."
    },
    "legalEntity": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "import/export: legal entity / company id, e.g. 'USMF'."
    },
    "packageName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "export: optional package name."
    },
    "projectName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "create_project/apply_filter: DMF project name."
    },
    "aotEntityName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "apply_filter: AOT entity name, e.g. 'VendVendorV2Entity'."
    },
    "entityRowName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "apply_filter: entity row label, e.g. 'Vendors V2'."
    },
    "operationType": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "create_project: 'Export' (default) or 'Import'."
    },
    "definitionGroupId": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "import/export: definitionGroupId (DMF project id)."
    },
    "pollTimeoutSeconds": {
      "type": "integer",
      "default": 300,
      "description": "import/export: max seconds to poll for completion. Default 300."
    }
  }
}
dmf annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
find_callers
Advertised by the server during the last successful anonymous check.
Advertised read-only

Full cross-reference profile for a class or method: callers, callees, inheritance chain, interface implementations, overrides, attributes. Requires xref_index.json.gz (generated from DYNAMICSXREFDB via GenerateCache --xref-conn). Falls back to vector-search when XRef not loaded.

find_callers input schema
{
  "type": "object",
  "required": [
    "methodKey"
  ],
  "properties": {
    "depth": {
      "type": "integer",
      "default": 2,
      "description": "Depth of caller chain to trace upward (default: 2, max: 4)"
    },
    "methodKey": {
      "type": "string",
      "description": "Object or method key. Format: 'ClassName.methodName' for methods, 'ClassName' for class-level queries."
    },
    "categories": {
      "type": "string",
      "default": "all",
      "description": "Categories to include: 'all' (default), or comma-separated subset: 'calls,inheritance,usage,interfaces,overrides,attributes'"
    },
    "maxResults": {
      "type": "integer",
      "default": 20,
      "description": "Maximum results per category (default: 20, max: 50)"
    }
  }
}
find_callers annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
healthcheck
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: checking server status, loaded D365 version, or custom model path. Triggers: 'status', 'statut', 'is the server ready', 'how many chunks', 'index loaded'. Returns JSON with: status, indexed chunk count, loaded version, custom model path.

healthcheck input schema
{
  "type": "object",
  "properties": {}
}
healthcheck output schema
{
  "type": "object",
  "required": [
    "result"
  ],
  "properties": {
    "result": {
      "type": "string"
    }
  }
}
healthcheck annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_analyze_pr_impact
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

[~] PRIORITY TRIGGER: Use this tool when the user says 'analyse PR', 'review PR', 'check PR', 'PR #', 'impact du PR', 'analyse la PR', 'what changed in PR', 'D365 impact of PR', 'code review PR', 'violations in PR', 'PR review'. NEVER call search_d365_code when 'PR' or 'Pull Request' + a number is mentioned. Analyse the full D365 F&O code impact of a Pull Request. Reads each changed file's CONTENT straight from the PR's source commit via the ADO REST API, so it reviews the PROPOSED (un-merged) code -- including brand-new files that do not yet exist on the target branch. DO NOT fall back to local `git show`/`git diff`: this tool already pulls the un-merged content over the API and analyses it against the indexed standard KB. When `includeSource` is true (the default), the FULL un-merged source of every analysed file is embedded in the output (one fenced block per file), so you have everything needed for a complete semantic review -- BP findings AND the actual code -- in a single call. NEVER run git to read the files. This matters for metadata-only PRs (tables/enums/menu items/reports): the BP engine is X++-centric and may report few violations on AOT XML, but the embedded source lets you review those changes properly. For every X++ class/table/form/extension modified in the PR: (1) Best Practice validation -- reports Critical and Warning violations. (2) Upgrade impact -- cross-references CoC targets, event handlers, and extensions against the D365 standard code. (3) Extension conflicts -- finds existing CoC/extensions that may conflict. (4) Produces a ready-to-post PR review comment addressed to the PR author. After reviewing, call `ado_post_pr_comment` to post the review (requires user confirmation). Requires DEVOPS_ORG_URL + DEVOPS_PAT (Code: Read).

ado_analyze_pr_impact input schema
{
  "type": "object",
  "required": [
    "repositoryId",
    "prId"
  ],
  "properties": {
    "prId": {
      "type": "integer",
      "description": "Pull Request ID (integer), e.g. 42."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "maxFilesDeep": {
      "type": "integer",
      "default": 10,
      "description": "Max X++ files to fully analyse (default 10, max 20). Larger PRs get a summary for remaining files."
    },
    "repositoryId": {
      "type": "string",
      "description": "Git repository name or ID."
    },
    "includeSource": {
      "type": "boolean",
      "default": true,
      "description": "Embed the un-merged SOURCE of each analysed file in the output so the reviewer can do a full semantic code review without a local git checkout. Default true."
    },
    "includeWarnings": {
      "type": "boolean",
      "default": true,
      "description": "Include Warning-level violations in addition to Critical (default: true). Set false for Critical-only."
    },
    "sourceCharsPerFile": {
      "type": "integer",
      "default": 8000,
      "description": "Max characters of source to embed per file when includeSource is true (1000-40000, default 8000). Larger files are truncated with a note."
    }
  }
}
ado_analyze_pr_impact annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
find_error_patterns
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: a user encounters an error message, infolog error, or runtime exception in D365. Also handles business-language error explanation when audienceType='business'. Triggers (developer): 'fix this error', 'what causes', 'exception thrown', 'infolog error', 'update conflict', 'outside tts', 'number sequence'. Triggers (business): 'what does this error mean', 'explain this error to me', 'user gets error X', 'que signifie cette erreur', 'message d\'erreur', 'what should the user do when they see this error'. Find known D365 F&O error patterns matching an error message or symptoms description. Matches against a built-in database of common errors (transaction conflicts, security issues, number sequences, posting errors, batch problems, etc.), resolves D365 label IDs from error text (e.g. user sees 'Number sequence not set up' -> finds @SYS70535 -> finds the throwing code), and searches the indexed codebase. Returns root causes, step-by-step resolution, label matches, and source code locations. [~] When the error text contains a D365 label ID (e.g. '@SYS12345'), call `search_labels` first to resolve the label text, then call this tool with the resolved text. [~] Also explains a Microsoft best-practice moniker: pass 'BPErrorFieldCurrencyDateTableEmpty' (or a symptom like 'strong name') to get the rule text from Microsoft's own catalogue, read out of the indexed D365 build. That is the rule TEXT -- `run_best_practices_check_scoped` is what decides whether an object breaks it. Set audienceType='business' for a plain-language explanation targeted at end users instead of developers.

find_error_patterns input schema
{
  "type": "object",
  "required": [
    "errorOrSymptom"
  ],
  "properties": {
    "audienceType": {
      "type": "string",
      "default": "developer",
      "description": "Audience type: 'developer' (default) for root-cause analysis, or 'business' for a plain-language explanation targeted at end users."
    },
    "errorOrSymptom": {
      "type": "string",
      "description": "Error message text or symptom description, e.g. 'update conflict', 'number sequence not set up', 'cannot create record outside tts'"
    }
  }
}
find_error_patterns annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
suggest_edt
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: adding a new field to a table -- find the best existing D365 EDT to extend instead of using raw primitives (str, int64, real, date). Triggers: 'what EDT for', 'which EDT should I extend', 'quel EDT pour', 'quel type étendu', 'EDT for a field'. D365 best practice mandates EDT reuse over raw primitive types. Call BEFORE declaring any field with a primitive type. Returns ranked candidate EDTs with their base type, label, and model.

suggest_edt input schema
{
  "type": "object",
  "required": [
    "fieldName",
    "purpose"
  ],
  "properties": {
    "topK": {
      "type": "integer",
      "default": 8,
      "description": "Number of EDT candidates to return (default: 8, max: 20)"
    },
    "purpose": {
      "type": "string",
      "description": "Purpose of the field in plain language, e.g. 'customer account number', 'approval status enum', 'invoice amount in transaction currency'"
    },
    "baseType": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: D365 primitive base type to filter by, e.g. 'str', 'int64', 'real', 'date', 'enum'. Leave empty to search all types."
    },
    "fieldName": {
      "type": "string",
      "description": "Field name or concept, e.g. 'AccountNum', 'vendorId', 'itemCode', 'approvalStatus'"
    }
  }
}
suggest_edt annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
get_object_context
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: you need a COMPLETE picture of a D365 object in ONE call. Returns in a single response: full structure (fields, method signatures, relation summary) AND all CoC extensions / event handlers -- equivalent to calling get_object_details THEN find_extensions. Use this INSTEAD of those two separate calls to reduce round-trips. Optionally includes best-practice violations (set includeValidation=true). Pass `methodName` to also include the full body of a specific method. Pass `aotType` to disambiguate when several AOT objects share the same name.

get_object_context input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "aotType": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: AOT type to disambiguate, e.g. 'AxTable', 'AxClass', 'AxForm'."
    },
    "methodName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: specific method name to include full body for."
    },
    "objectName": {
      "type": "string",
      "description": "Exact object name, e.g. 'SalesTable', 'CustTable', 'VendInvoiceJour'"
    },
    "includeValidation": {
      "type": "boolean",
      "default": false,
      "description": "Include best-practice violations (default false -- adds latency for large objects)."
    }
  }
}
get_object_context annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
odata_upsert_rows
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

Idempotent import of rows into any entity via OData: PATCH when the record exists (matched by keyFields), otherwise POST. Safe to re-run -- duplicates are updated, not re-created. Best for small-to-medium transactional loads (e.g. <= a few thousand rows). For bulk loads use dmf_import_file. Provide rows as a JSON array of objects; resolve key fields from the KB (get_data_entity_info) -- do not guess them.

odata_upsert_rows input schema
{
  "type": "object",
  "required": [
    "entitySet",
    "rowsJson",
    "keyFields"
  ],
  "properties": {
    "rowsJson": {
      "type": "string",
      "description": "JSON array of row objects, e.g. [{\"CustomerAccount\":\"C0001\",\"Name\":\"Acme\"}]."
    },
    "entitySet": {
      "type": "string",
      "description": "OData public entity set name, e.g. 'CustomersV3'."
    },
    "keyFields": {
      "type": "string",
      "description": "Comma-separated business key fields used to detect existing records, e.g. 'CustomerAccount' or 'dataAreaId,ItemNumber'."
    },
    "legalEntity": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional legal entity (dataAreaId) injected into each row when absent."
    },
    "crossCompany": {
      "type": "boolean",
      "default": true,
      "description": "Set true to allow cross-company writes."
    }
  }
}
odata_upsert_rows annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": true
}
find_change_impact
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: about to modify an X++ object or method and need to understand what could break. Returns a blast-radius report: direct callers, transitive dependents (BFS up to 3 hops), custom model extensions, interface propagation, and severity classification (Critical/High/Medium/Low). Requires the XRef index (loaded from xref_index.json.gz). Triggers: 'what breaks if I change X', 'impact of modifying SalesLine', 'blast radius', 'refactoring risk', 'quel impact si je modifie', 'qu\'est-ce qui dépend de'. Pass objectName='SalesLine' for table/class-level impact, or objectName='SalesLine.insert' for method-level impact. Combine with validate_best_practices / detect_performance_issues to get both impact AND current code quality before committing a change.

find_change_impact input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "maxDepth": {
      "type": "integer",
      "default": 3,
      "description": "Max BFS depth (1-5). Default 3."
    },
    "objectName": {
      "type": "string",
      "description": "Object or method to analyse, e.g. 'SalesLine' or 'SalesLine.insert'."
    }
  }
}
find_change_impact annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
generate_security_report
Advertised by the server during the last successful anonymous check.
Advertised read-only

Generate a security & licensing governance report for the indexed model(s). For each role, lists duties, privileges, entry points and the inferred user-license tier (Activity, Operations, Finance, etc.). Surfaces orphan duties/privileges that grant no real access.

generate_security_report input schema
{
  "type": "object",
  "properties": {
    "filterModel": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: restrict the report to a single model name (e.g. 'ApplicationSuite', 'ContosoCustom'). Empty = all indexed models."
    },
    "maxRolesDetail": {
      "type": "integer",
      "default": 50,
      "description": "Maximum number of roles to expand in detail (default 50, max 200)."
    }
  }
}
generate_security_report annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_create_task
Advertised by the server during the last successful anonymous check.
Open-world

WHEN: user asks to create a DevOps Task or start development on a Work Item. Triggers: 'create task', 'créer une tâche', 'start development on', 'commencer le dev sur', 'créer un task ADO', 'new development task for WI'. Create a D365 F&O development Task work item in Azure DevOps following the project naming convention. Rules applied automatically based on the parent work item: (A) Parent title starts with a project prefix + digits (FDD/RDD/IDD/CR/...) => Task named '{Prefix} - N - {Title}', Task is child of that WI, branch '{Prefix}_Task_{id}'. (B) Selected WI is related to a prefixed WI (e.g. a Bug linked to a CR) => Task is child of the prefixed WI, Bug is 'related' to the Task, same naming. (C) No prefixed relation => Task named '{3-letter-type}{wiId} - N - {Title}', child of selected WI, branch '{3-letter-type}{wiId}_Task_{id}'. The N counter increments automatically based on existing sibling tasks. A Git branch is created automatically in the repository. Assignee and CC list are embedded in the task description. LANGUAGE RULE: write the 'description' (Proposed Solution) in the same language the user used in their request. Default is English. If the user wrote in French, write the solution in French. Static section labels (headers, field names) remain in English. Requires DEVOPS_ORG_URL + DEVOPS_PAT (Work Items: Read & Write, Code: Read & Write).

ado_create_task input schema
{
  "type": "object",
  "required": [
    "workItemId"
  ],
  "properties": {
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "areaPath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: area path for the new task, e.g. 'MyProject\\SER'. Inherits from parent if omitted."
    },
    "assignTo": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: email address of the person to assign the task to, e.g. 'dev@company.com'."
    },
    "notifyList": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: comma-separated list of email addresses to notify (added to task description), e.g. 'lead@company.com,qa@company.com'."
    },
    "workItemId": {
      "type": "integer",
      "description": "ID of the selected Work Item (parent, bug, issue, etc.) to base the task on."
    },
    "description": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: additional description / acceptance criteria text to append to the task."
    },
    "effortHours": {
      "type": [
        "integer",
        "null"
      ],
      "default": null,
      "description": "Optional: estimated effort in hours (Original Estimate)."
    },
    "repositoryId": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Git repository name to create the branch in. If omitted, uses the first repository found."
    },
    "sourceBranch": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: source branch to branch from (default: repo default branch, e.g. 'main')."
    },
    "iterationPath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: iteration path for the new task, e.g. 'MyProject\\Sprint 5'. Inherits from parent if omitted."
    }
  }
}
ado_create_task annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
appinsights_set_connection
Advertised by the server during the last successful anonymous check.

Securely register the D365 F&O environment's Application Insights / Log Analytics connection for the CURRENT session. The client secret is encrypted in memory (AES-256-GCM), never written to disk and never echoed back. Once set, appinsights_query and appinsights_diagnose_slowness use it automatically until it expires or you call appinsights_clear_connection. HOW TO GET THE VALUES: workspaceId -- the Log Analytics WORKSPACE ID (GUID, not the App Insights app id) behind the Application Insights resource the environment is linked to (D365FO: System administration > Monitoring and Telemetry parameters > Application Insights Registry tab shows the connection string; the workspace id is on that Log Analytics workspace resource's Overview blade in the Azure Portal). tenantId/clientId/clientSecret -- an Entra ID app registration granted the 'Log Analytics Reader' (or 'Monitoring Reader') role on that workspace resource (Azure Portal > workspace > Access control (IAM) > Add role assignment). Read-only -- no write access is ever needed or used. In a locked server deployment (APPINSIGHTS_LOCK_SERVER_CONFIG=true) this tool is disabled and the server's own environment credentials are used instead.

appinsights_set_connection input schema
{
  "type": "object",
  "required": [
    "workspaceId",
    "tenantId",
    "clientId",
    "clientSecret"
  ],
  "properties": {
    "clientId": {
      "type": "string",
      "description": "App registration (client) id (GUID), granted Log Analytics Reader on the workspace."
    },
    "tenantId": {
      "type": "string",
      "description": "Entra (Azure AD) tenant id (GUID)."
    },
    "ttlMinutes": {
      "type": "integer",
      "default": 60,
      "description": "Minutes the connection stays cached for this session (1-480). Default 60."
    },
    "workspaceId": {
      "type": "string",
      "description": "Log Analytics workspace ID (GUID) behind the linked Application Insights resource."
    },
    "clientSecret": {
      "type": "string",
      "description": "App registration client secret. Encrypted in memory; never logged or persisted."
    }
  }
}
appinsights_set_connection annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
erp_list_instances
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: first step whenever the user wants to read from or act on a Dynamics 365 F&O (ERP) environment through this server. Lists the F&O instances you may use (alias, label, whether it is protected like PROD, whether it is readOnly, and whether the user has already connected it). Use the alias as the 'instance' argument of erp_list_tools and erp_call. If connected=false, tell the user to open the connectUrl in a browser, then retry.

erp_list_instances input schema
{
  "type": "object",
  "properties": {}
}
erp_list_instances annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
dmf_get_job_status
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Poll the status of a DMF import/export execution by its executionId (e.g. NotRun, Executing, Succeeded, PartiallySucceeded, Failed). For a completed export, also returns the download URL.

dmf_get_job_status input schema
{
  "type": "object",
  "required": [
    "executionId"
  ],
  "properties": {
    "executionId": {
      "type": "string",
      "description": "The executionId returned by dmf_import_file or dmf_export_package."
    }
  }
}
dmf_get_job_status annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
detect_performance_issues
Advertised by the server during the last successful anonymous check.
Advertised read-only

Profile an X++ object for N+1 queries, queries in loops, missing field lists, row-by-row inserts/updates, missing firstOnly. Returns compact issue table with line + fix. Only call when performance is explicitly the concern — for general quality use validate_best_practices. [!] Auto-fixing requires D365_CUSTOM_MODEL_PATH (custom code only).

detect_performance_issues input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "methodName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: specific method to analyze. Analyzes all methods if not provided."
    },
    "objectName": {
      "type": "string",
      "description": "Object name to analyze, e.g. 'SalesTable', 'CustInvoiceJour'"
    }
  }
}
detect_performance_issues annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
get_relation_graph
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: you need the COMPLETE bidirectional relation graph for an object in ONE call. Triggers: 'relations of', 'FK of', 'what tables link to', 'quelles tables liées à', 'avant de générer du code', 'before generating code', 'foreign keys', 'delete actions', 'who references', 'qui référence', 'graph de relations'. Returns ALL outgoing edges (FK relations, DeleteActions, DataSources, Extensions, Security...) AND all incoming back-references (forms, entities, CoC classes, privileges... that reference it). Backed by the pre-computed relation index -- O(1) lookup, no vector scan. Much faster and more complete than find_related_objects for known object names. ALWAYS call this before generating code that touches multiple objects or requires join logic. Use find_related_objects when the relation index is not yet built (fallback to vector scan).

get_relation_graph input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "maxEdges": {
      "type": "integer",
      "default": 200,
      "description": "Maximum edges to show per direction (default: 200, max: 500)"
    },
    "objectName": {
      "type": "string",
      "description": "The exact object name, e.g. 'SalesTable', 'CustTable', 'SalesFormLetter'"
    },
    "aotTypeFilter": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter edges by relation kind to reduce noise. Comma-separated. Examples: 'TableFK', 'DeleteAction', 'Extension', 'DataSource', 'Security'. Leave empty for all kinds."
    }
  }
}
get_relation_graph annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
d365fo_clear_connection
Advertised by the server during the last successful anonymous check.

Removes the D365 F&O connection cached for the current session by d365fo_set_connection.

d365fo_clear_connection input schema
{
  "type": "object",
  "properties": {}
}
d365fo_clear_connection annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_find_tests_for_objects
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

WHEN: a change touched some D365 objects (a pull request, a hotfix) and you need the test cases to replay. Triggers: 'which tests cover SalesLine', 'quels tests rejouer', 'regression tests for PR N', 'couverture de test des objets'. Finds the Test Case work items tagged obj:<object> (the tag ado_create_test_plan adds from `objects`), per object, and lists the objects that no test case covers: those are coverage gaps, not proof that nothing can break. Only cases created with the obj: tags are found; older ones are not. Read only. At most 50 cases.

ado_find_tests_for_objects input schema
{
  "type": "object",
  "required": [
    "objects"
  ],
  "properties": {
    "objects": {
      "type": "string",
      "description": "Comma-separated D365 objects, e.g. 'SalesLine, CustInvoiceJour'. Take them from the pull request or find_change_impact."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Azure DevOps project. Falls back to DEVOPS_PROJECT."
    }
  }
}
ado_find_tests_for_objects annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
generate_xpp_template
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: writing an extension or customization -- generates ready-to-use X++ code. Triggers: 'génère un CoC', 'crée une extension', 'generate extension', 'write a CoC class', 'event handler pour', 'template pour'. Uses REAL metadata from the KB (actual field names, method signatures). 'coc' = Chain of Command class, 'table_extension' = extend table with fields/methods, 'event_handler' = pre/post event handler, 'job' = runnable class, 'find_method' = find/exist pattern. ALWAYS call get_object_details first to verify the object exists.

generate_xpp_template input schema
{
  "type": "object",
  "required": [
    "templateType",
    "objectName"
  ],
  "properties": {
    "methodName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: specific method name for CoC or event handler templates"
    },
    "objectName": {
      "type": "string",
      "description": "The base object, e.g. 'SalesTable', 'VendInvoiceJour'"
    },
    "templateType": {
      "type": "string",
      "description": "Template type: 'coc', 'table_extension', 'event_handler', 'job', 'find_method'"
    }
  }
}
generate_xpp_template annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
lesson_search
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: BEFORE investigating a problem, designing a change or answering a question about how a client's D365 F&O works: check whether it was already solved. Returns the lessons learned that best match, with their problem, cause and solution. Triggers: 'did we already see this', 'a-t-on déjà eu ce problème', 'known issue', 'leçons apprises', 'how did we fix'. Pass the D365 objects involved in `objects` when you know them: they weigh most. A result marked UNVALIDATED has not been reviewed by an administrator: use it as a lead and verify it. A lesson records a past case: confirm the cause is the same before applying its solution, and say which lesson you used.

lesson_search input schema
{
  "type": "object",
  "required": [
    "query"
  ],
  "properties": {
    "query": {
      "type": "string",
      "description": "The symptom, error message or question, in plain words."
    },
    "objects": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: comma-separated D365 objects involved, e.g. 'SalesLine, CustInvoiceJour'."
    },
    "maxResults": {
      "type": "integer",
      "default": 5,
      "description": "Maximum lessons to return (1-10, default 5)."
    }
  }
}
lesson_search annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
get_security_coverage_for_object
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer/security architect needs to know WHICH ROLES can access a specific form, table, menu item or service operation. Triggers: 'who can access', 'which roles see', 'security coverage for', 'quels roles ont accès à', 'find roles with access'. Walks the security graph backwards (EntryPoint -> Privilege -> Duty -> Role) and returns all roles that grant any level of access (Read / Update / Create / Delete / Correct) on the given object. Read-only: scans the in-memory KB, never writes.

get_security_coverage_for_object input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "maxRoles": {
      "type": "integer",
      "default": 30,
      "description": "Maximum roles to expand (default 30, max 100)."
    },
    "minGrant": {
      "type": "string",
      "default": "Any",
      "description": "Optional: minimum grant level to include in results. Values: 'Read', 'Update', 'Create', 'Delete', 'Correct', 'Any'. Default: 'Any'."
    },
    "objectName": {
      "type": "string",
      "description": "Object name to audit (form, table, menu item, service operation). Example: 'CustTable', 'SalesTableListPage', 'CustCustomerServiceMenu'."
    }
  }
}
get_security_coverage_for_object annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
get_output_page
Advertised by the server during the last successful anonymous check.
Advertised read-only

Retrieve the NEXT page of a previously PAGINATED tool output. When a tool result is too large for a single response, it is split losslessly into ordered pages — the footer of each page gives you a `token` and the next `page` number. Call this tool with that token to read the continuation; nothing is dropped, so you can walk every page in order and reconstruct the full result. Pages are cached only briefly (the last few large results). If the token is unknown or expired, re-run the original tool to regenerate it.

get_output_page input schema
{
  "type": "object",
  "required": [
    "token"
  ],
  "properties": {
    "page": {
      "type": "integer",
      "default": 2,
      "description": "The page number to retrieve (the previous page footer tells you the next one). Defaults to 2."
    },
    "token": {
      "type": "string",
      "description": "The pagination token from a paginated tool's footer, e.g. 'op_a1b2c3d4e5'."
    }
  }
}
get_output_page annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_pr_dependency_map
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

PR DEPENDENCY MAP -- Scan multiple Pull Requests and build a cross-PR dependency graph based on (a) shared X++/AOT objects and (b) branch chain relationships. For each PR: * Lists X++ / AOT objects changed (from diff) * Detects OBJECT CONFLICTS: same object modified in multiple PRs => merge risk * Detects BRANCH CHAIN: if PR_A.targetBranch == PR_B.sourceBranch => PR_A must merge first * Computes RECOMMENDED MERGE ORDER (topological sort by branch dependencies) Output: * Per-PR object table * Conflict matrix (object -> [PR list]) * Dependency graph summary * Ordered merge sequence Triggers: 'PR dependencies', 'ordre de merge des PR', 'conflits entre PR', 'quelles PR touche le même objet', 'dependency map PRs', 'merge order PRs', 'list PRs with objects', 'objets par PR', 'cross-PR impact'. Requires DEVOPS_ORG_URL + DEVOPS_PAT (Code: Read scope).

ado_pr_dependency_map input schema
{
  "type": "object",
  "required": [
    "repositoryId"
  ],
  "properties": {
    "top": {
      "type": "integer",
      "default": 30,
      "description": "Maximum PRs to analyse (default: 30, max: 60)."
    },
    "status": {
      "type": "string",
      "default": "Active",
      "description": "PR status filter: Active (default), Completed, Abandoned, All."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "repositoryId": {
      "type": "string",
      "description": "Git repository name or ID."
    },
    "targetBranch": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by target branch, e.g. 'develop'."
    }
  }
}
ado_pr_dependency_map annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
generate_fdd
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: user asks to write or generate a Functional Design Document, FDD, functional spec, CdC, or cahier des charges. NOT for developer technical docs -- use `get_object_details` for that. FUNCTIONAL DESIGN DOCUMENT GENERATOR -- Produces a structured FDD ready for review and sign-off. Sections generated: Purpose, Business Context, Data Fields (with resolved labels), Business Rules, Related Objects, Security, and Open Questions. Triggers: 'write FDD for', 'generate FDD', 'functional spec for', 'document this process', 'write functional design', 'rédiger le cahier des charges', 'CdC pour', 'fiche de conception'.

generate_fdd input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "context": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: additional business context, requirements, or audience note to include in the header"
    },
    "language": {
      "type": "string",
      "default": "en",
      "description": "Optional: output language ('en', 'fr', 'nl', 'de'). Default: en"
    },
    "objectName": {
      "type": "string",
      "description": "D365 object or business process name, e.g. 'SalesTable', 'VendInvoiceInfoTable', 'ALMDemandeAchat'"
    }
  }
}
generate_fdd annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
map_business_process
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: mapping the technical D365 objects behind a business process, or understanding which tables/forms implement a flow. Triggers: 'processus métier', 'Order-to-Cash', 'Procure-to-Pay', 'Record-to-Report', 'business process flow', 'qui est impliqué dans', 'map the process', 'flux du processus', 'quels objets dans le flux'. Map a D365 F&O business process to its complete object chain. For known processes (Order-to-Cash, Procure-to-Pay, Record-to-Report, Plan-to-Produce, Inventory-Management, Hire-to-Retire, Project-Accounting, Asset-Lifecycle): shows every step with forms, tables, classes, entities, reports, and security roles involved. For any other object name: traces all dependencies (tables, classes, forms, entities) from that entry point. Produces a Mermaid process flow diagram. Use 'list' to see all known process mappings. NOT for a single object's FK relations only -- use `find_related_objects` for that (faster and more precise).

map_business_process input schema
{
  "type": "object",
  "required": [
    "processName"
  ],
  "properties": {
    "processName": {
      "type": "string",
      "description": "Business process name (e.g. 'Order-to-Cash', 'Procure-to-Pay', 'sales', 'purchasing') or an object name to trace from. Use 'list' to see all known processes."
    }
  }
}
map_business_process annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_post_comment
Advertised by the server during the last successful anonymous check.
Open-world

WHEN: user explicitly asks to post, add, or save a comment to an ADO Work Item. [~] PRIORITY TRIGGER: call AFTER `ado_analyze_workitem` when user says 'post the analysis', 'save it to the ticket', 'ajoute en commentaire'. WARNING: ALWAYS ask for explicit user confirmation before calling this tool. Recommended workflow: (1) call `ado_analyze_workitem`, (2) show analysis to the user, (3) ask 'Shall I post this comment to Work Item #X?', (4) only then call this tool. Requires DEVOPS_ORG_URL + DEVOPS_PAT with Work Items: Write permission.

ado_post_comment input schema
{
  "type": "object",
  "required": [
    "workItemId",
    "commentText"
  ],
  "properties": {
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "workItemId": {
      "type": "integer",
      "description": "Work item ID (integer), e.g. 1234"
    },
    "commentText": {
      "type": "string",
      "description": "Markdown text to post. Use the ready-to-post block from `ado_analyze_workitem`."
    }
  }
}
ado_post_comment annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
get_data_entity_info
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer building an OData / DMF integration needs a quick rundown of a specific data entity: its public OData name, datasources, key fields, and IsPublic status. Triggers: 'data entity info', 'OData entity details', 'is X a public entity', 'entity datasources'. Cloud-safe: pure metadata read from the KB.

get_data_entity_info input schema
{
  "type": "object",
  "required": [
    "entityName"
  ],
  "properties": {
    "entityName": {
      "type": "string",
      "description": "Data entity name (AxDataEntityView), e.g. 'CustCustomerV3Entity', 'SalesOrderHeaderV2Entity'."
    }
  }
}
get_data_entity_info annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
compare_objects
Advertised by the server during the last successful anonymous check.
Advertised read-only

Compare two D365 F&O objects side-by-side (fields, methods, signatures). Handy when investigating differences between standard and a customisation, or between two similar tables.

compare_objects input schema
{
  "type": "object",
  "required": [
    "objectNameA",
    "objectNameB"
  ],
  "properties": {
    "objectNameA": {
      "type": "string",
      "description": "First object name, e.g. 'SalesTable'"
    },
    "objectNameB": {
      "type": "string",
      "description": "Second object name, e.g. 'PurchTable'"
    }
  }
}
compare_objects annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
list_objects
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: you need ALL objects of a given type or in a given model. Triggers: 'list all tables in ALM', 'show all classes', 'quels objets dans le modèle', 'give me all forms'. Full index scan -- returns EVERY matching object, not just top search results. Use to discover what tables, classes, forms, enums, etc. exist in a specific model. When no filters are given and a custom model is configured, defaults to listing that model. NOT for a single object -- use get_object_details. NOT for natural language search -- use search_d365_code.

list_objects input schema
{
  "type": "object",
  "properties": {
    "aotType": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Filter by AOT type: AxClass, AxTable, AxForm, AxView, AxEnum, AxEdt, AxDataEntityView, AxSecurityPrivilege, AxSecurityDuty, AxSecurityRole, AxQuery, AxReport, Documentation. Leave empty for all types."
    },
    "modelName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Filter: matches BOTH package/module name (partial) AND object name (substring). Examples: 'ALM' (package), 'Cust' (any object containing 'Cust'), 'SalesTable'. Leave empty for all."
    }
  }
}
list_objects annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
explain_workflow
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: user asks how an approval workflow works, who approves a document, what states it goes through, or what happens on submission/rejection. NOT for technical workflow class details -- use `get_object_details`. WORKFLOW EXPLAINER (Business Language) -- Explains a D365 approval workflow: who approves, what states exist, and what happens on approval or rejection. Output is plain business language -- no X++ or workflow engine jargon. Triggers: 'explain the workflow for', 'how does the approval work', 'qui approuve', 'workflow states for', 'étapes du workflow', 'approval process for', 'circuit d\'approbation', 'what happens when a user submits'.

explain_workflow input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "objectName": {
      "type": "string",
      "description": "D365 object or workflow name, e.g. 'SalesTable', 'PurchTable', 'ALMDemandeAchatWorkflow'"
    }
  }
}
explain_workflow annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_test_status
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

WHEN: the user asks whether a feature is tested and what the results are: 'is WI N tested', 'where are we on the tests', 'état des tests de la story', 'what failed'. Lists the test cases tied to a work item with their latest outcome in its test plan suite (Passed, Failed, Blocked, Not run) and a summary. Read only. Pass planId when you know it (ado_create_test_plan returns it); otherwise the plan named 'Tests - WI {id} ...' is looked for. A test case that is not in any suite of the plan shows as 'not in the plan'.

ado_test_status input schema
{
  "type": "object",
  "required": [
    "workItemId"
  ],
  "properties": {
    "planId": {
      "type": [
        "integer",
        "null"
      ],
      "default": null,
      "description": "Optional: the test plan id."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Azure DevOps project. Falls back to DEVOPS_PROJECT."
    },
    "workItemId": {
      "type": "integer",
      "description": "Work item the tests verify, e.g. 6587."
    }
  }
}
ado_test_status annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
trace_role_license_tree
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: security design, licence audit, or 'what licence does this role require?'. Triggers: 'arborescence du rôle', 'licence nécessaire pour', 'what licence for role', 'role tree', 'droits du rôle', 'entry points of role', 'privilege tree for'. Builds the COMPLETE tree for ONE role: Role -> Duties -> Privileges -> Entry Points. For each entry point, classifies the required D365 licence per the March 2026 Licensing Guide: Team Members (~$8/user/mo, read-only + named tasks), Operations-Activity (~$50, warehouse mobile & production floor), Finance (~$180), Supply Chain Mgmt (~$180), Human Resources (~$22), Project Operations (~$120), Commerce (~$180). Grant-level aware: NoAccess/Read/View -> Team Members; Activity writes -> Operations-Activity; transactional writes -> full product licence based on functional area. Confidence: High (known module prefix) . Medium (keyword) . Low (fallback). Ends with a Optimization section: Team Members / Activity eligibility, role-split opportunities, per-user/month cost estimates (March 2026 MSRP). Always validate against the [Microsoft D365 Licensing Guide](https://go.microsoft.com/fwlink/?LinkId=866544). For a full multi-role scan, call `trace_role_license_tree` multiple times -- once per role. NOT for the pure technical duty/privilege/entry-point chain without licence inference -- use `trace_security_chain` for that.

trace_role_license_tree input schema
{
  "type": "object",
  "required": [
    "roleName"
  ],
  "properties": {
    "roleName": {
      "type": "string",
      "description": "The exact security role name, e.g. 'SystemAdministrator', 'AccountsPayablePaymentsClerk', 'ALMMyCustomRole'"
    },
    "maxEntryPoints": {
      "type": "integer",
      "default": 20,
      "description": "Maximum entry points to show per privilege (default: 20, max: 100)"
    }
  }
}
trace_role_license_tree annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
prepare_release_note_context
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: building an AI-assisted D365 F&O upgrade release note (regressions + opportunities) for a specific client, and you (the calling assistant) want to do the reasoning yourself instead of the server calling its own LLM. Call resolve_client_profile FIRST -- if it finds a profile, OMIT v1/v2/customModelIds here and they will be auto-filled from it. If no profile exists, call list_release_note_inputs to get real values and pass them explicitly -- never guess them. Triggers: 'release note', 'upgrade impact for this client', 'what breaks for a client between these versions', 'regression risk', 'note de version'. Diffs two indexed D365FO versions (v1=older, v2=newer) and cross-references EVERY changed standard object against ALL the given custom models (a client can have several -- their own extensions AND a separate ISV vendor model, in which case pass both ids comma-separated) -- returning ONLY the subset of changes actually touched by the client's code (capped at 60, Removed > Modified > Added priority), each with old/new content and which custom object references it. Returns a JSON payload with an 'instructions' field telling you the EXACT schema to produce -- analyze the 'objects' array yourself, then call generate_release_note_document with your findings JSON to get the downloadable Word/PowerPoint.

prepare_release_note_context input schema
{
  "type": "object",
  "properties": {
    "v1": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Older/baseline D365FO version to compare FROM, e.g. \"10.0.2527.109\". Omit to auto-fill from the caller's resolved client profile (see resolve_client_profile)."
    },
    "v2": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Newer D365FO version to compare TO, e.g. \"10.0.2645.32\". Omit to auto-fill from the caller's resolved client profile."
    },
    "customModelIds": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Comma-separated custom model id(s) from the Admin > Custom Models tab. Omit to auto-fill from the caller's resolved client profile. Pass several when a client combines their own extensions with a separate ISV vendor model."
    },
    "businessContext": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional free-text business/functional context about the client (modules used, key customizations, priorities) to sharpen the opportunity/regression assessment."
    }
  }
}
prepare_release_note_context annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_analyze_workitem
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

AZURE DEVOPS ONLY -- Fetch a Work Item and assemble ALL technical context needed for D365 F&O expert analysis. [~] PRIORITY TRIGGER: 'analyse le workitem', 'analyse la tâche', 'analyse le FDD/RDD/CR/IDD', 'read the work item', 'check the bug', 'look at ticket', 'review task', '#1234', 'WI#', 'WI ', 'item #'. NEVER for: labels (@SYS/@TRX/@FIN), X++ code lookup, AOT objects -- use search_labels / search_d365_code instead. ## WHAT THIS TOOL RETURNS Raw structured context only -- NOT a finished analysis. The tool returns: 1. Work item metadata (title, description, repro steps, acceptance criteria, comments) 2. D365 standard KB object details: fields, methods, code snippets for every matched object 3. Custom code on disk (customer extension model): existing CoC methods, extension bodies 4. Chain of Command / relation graph for all impacted objects ## YOUR JOB AS COPILOT AFTER CALLING THIS TOOL You MUST synthesize the raw context into a precise developer-ready analysis IN FRENCH. Write it in a professional tone, as if authored by a senior D365 consultant -- no emojis, no icons. The analysis must contain these sections: 1. **Compréhension du besoin** -- résume ce que le client demande en 2-3 phrases claires 2. **Analyse technique** -- identifie la cause racine en croisant le besoin + les objets KB + le code custom 3. **Instructions de développement** -- liste ordonnée et précise : quel objet, quelle méthode, quoi modifier - Si une extension custom existe sur disque -> pointer exactement quelle méthode à modifier - Si pas d'extension -> indiquer quel CoC créer, sur quel objet standard, quelle méthode 4. **Estimation** -- chiffrage en heures/jours selon la complexité détectée 5. **Commentaire ADO** -- Texte markdown sans icônes, prêt à poster sur le WI analysé UNIQUEMENT. IMPORTANT: never post (never call ado_post_comment) on any linked/related work item -- only on the analyzed WI. Requires DEVOPS_ORG_URL + DEVOPS_PAT env vars.

ado_analyze_workitem input schema
{
  "type": "object",
  "required": [
    "workItemId"
  ],
  "properties": {
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "workItemId": {
      "type": "integer",
      "description": "Work item ID (integer), e.g. 1234"
    },
    "maxComments": {
      "type": "integer",
      "default": 5,
      "description": "Number of recent comments to include (default 5, max 20). Use 3 for faster results."
    },
    "focusObjects": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Comma-separated D365 object names to force-include in KB analysis, e.g. 'SalesTable,CustTable'. Providing these speeds up analysis significantly."
    },
    "sourceBranch": {
      "type": "string",
      "default": "main",
      "description": "Git branch to read custom code from when no PR or commit is linked to the work item. Default: 'main'. Use this to point at the integration branch (e.g. 'develop', 'release/2025')."
    },
    "includeImages": {
      "type": "boolean",
      "default": false,
      "description": "Include image attachments as base64 data URIs for visual analysis by Copilot. Default: false. Set true only when screenshots are needed -- adds latency and token cost."
    }
  }
}
ado_analyze_workitem annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
erp_call_session
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

WHEN: to run several ERP tools in ONE ERP session, in order, when the steps depend on each other: open a form, click New, set field values, save, close. Each separate erp_call starts a new ERP session, so a form opened by one call is gone at the next; this tool keeps them together. The user's own F&O roles apply; a read-only instance refuses every step that could change data. ON EVERY INSTANCE, steps that can change data run only with confirm=true: without it nothing runs and you get the exact steps back, to show to the user. Set confirm=true only after the user agreed to those exact steps. All steps are checked BEFORE the first one runs, and the run stops at the first step that fails, so nothing is written after a failure. stepsJson: [{"tool":"form_open_menu_item","arguments":{...}}, ...], at most 15 steps. Close the forms you open.

erp_call_session input schema
{
  "type": "object",
  "required": [
    "instance",
    "stepsJson"
  ],
  "properties": {
    "confirm": {
      "type": "boolean",
      "default": false,
      "description": "Required true to run steps that can change data, on any instance, and only after the user agreed to those exact steps."
    },
    "instance": {
      "type": "string",
      "description": "Instance alias from erp_list_instances."
    },
    "stepsJson": {
      "type": "string",
      "description": "JSON array of steps: [{\"tool\":\"<name from erp_list_tools>\",\"arguments\":{...}}]."
    }
  }
}
erp_call_session annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": true
}
ado_wiki_create_or_update_page
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

AZURE DEVOPS ONLY -- Create a new wiki page, or OVERWRITE an existing one with new markdown content. ALWAYS call ado_wiki_get_page first if you intend to edit (not replace) an existing page's content, since this tool replaces the ENTIRE page body -- it does not merge/append. Automatically handles the page's ETag for safe concurrent-edit detection. Requires DEVOPS_ORG_URL + DEVOPS_PAT env vars (PAT needs Wiki: Read & Write scope).

ado_wiki_create_or_update_page input schema
{
  "type": "object",
  "required": [
    "path",
    "content"
  ],
  "properties": {
    "path": {
      "type": "string",
      "description": "Wiki page path, e.g. '/Home' or '/Architecture/Deployment'. Parent pages are created automatically if missing."
    },
    "content": {
      "type": "string",
      "description": "Full markdown content for the page (replaces any existing content)."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "wikiIdentifier": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Wiki name or id. Omit to auto-resolve to the project's only/first wiki (call ado_wiki_list if there are several)."
    }
  }
}
ado_wiki_create_or_update_page annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": true
}
erp_read
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

WHEN: to READ from ONE Dynamics 365 F&O instance, as the signed-in user (their own F&O roles and legal entities apply): look up entities, query data, open a form to read it. Same arguments as erp_call, but it refuses every tool that could change data, on every instance, so it never needs the user's approval. Get the tool name and argument schema from erp_list_tools first. Use erp_call only for a change the user asked for.

erp_read input schema
{
  "type": "object",
  "required": [
    "instance",
    "tool"
  ],
  "properties": {
    "tool": {
      "type": "string",
      "description": "Exact tool name from erp_list_tools."
    },
    "instance": {
      "type": "string",
      "description": "Instance alias from erp_list_instances."
    },
    "arguments": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "JSON object with the tool arguments, as a string. Omit or \"{}\" when none."
    }
  }
}
erp_read annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
resolve_workspace_roots
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: you need to know which folder(s) are configured as the workspace/project root for the current caller. Reads D365-Custom-Model-Path and D365-Standard-Model-Path from the request headers or environment variables. Use before any tool that accepts a customModelPath/standardModelPath parameter.

resolve_workspace_roots input schema
{
  "type": "object",
  "properties": {}
}
resolve_workspace_roots annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
resolve_client_profile
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: at the START of any release-note / upgrade-impact conversation -- call this BEFORE list_release_note_inputs to check whether a Client Profile already exists for the CURRENT caller's Azure DevOps org/project (auto-detected from DEVOPS_ORG_URL/DEVOPS_PROJECT MCP headers -- you don't pass anything). If found, it gives you the client's currentVersionTag, targetVersionTag, customModelIds (their own extensions AND any attached ISV/vendor models) and the latest diff snapshot in one call -- use those directly as v1/v2/customModelIds for prepare_release_note_context, no further discovery needed. If not found, fall back to list_release_note_inputs and/or save_client_profile.

resolve_client_profile input schema
{
  "type": "object",
  "properties": {}
}
resolve_client_profile annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
generate_unit_test
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer needs to write or scaffold unit tests for a custom D365 object. Triggers: 'generate tests', 'unit test', 'SysTest', 'write test for', 'scénarios de test', 'test this class'. Generate X++ SysTest unit test code for a CUSTOM D365 F&O object based on functional test scenarios. [!] Only meaningful on custom/extension code (D365_CUSTOM_MODEL_PATH). SysTest tests in D365 are highly context-specific -- a generic template rarely compiles without adaptation. REQUIRED: provide test scenarios in the 'testScenarios' parameter (supplied by the functional consultant). Each scenario becomes a concrete test method with arrange/act/assert. For tables: generates tests for find(), exist(), validateWrite(), initValue(). For classes: generates stubs for each public method listed in scenarios. Uses REAL field names and method signatures from the knowledge base.

generate_unit_test input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "methodName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: specific method to test. If not provided, generates tests for all testable methods."
    },
    "objectName": {
      "type": "string",
      "description": "Object name to generate tests for, e.g. 'ALMERSftpConnectionTable', 'ALMMyClass'"
    },
    "sampleValues": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: JSON object mapping field name to sample value used in each ARRANGE block, e.g. '{\"AccountNum\":\"C0001\",\"Amount\":1500.50}'. Replaces the 'TODO: set up test data' placeholders with concrete assignments."
    },
    "testScenarios": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Test scenarios provided by the functional consultant, e.g. 'Create a connection with valid SFTP host; Validate that empty host fails; Delete cleans up related records'. Separate scenarios with semicolons."
    }
  }
}
generate_unit_test annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
create_aot_object
Advertised by the server during the last successful anonymous check.
Advertised read-only

Generate a complete, ready-to-deploy D365 F&O AOT XML scaffold for any object type. Returns the XML + the correct file path inside the model -- the calling client (VS Extension, Cursor, Copilot Chat) is responsible for writing the result to disk. This tool is fully read-only on the server: it never writes to the filesystem, never mutates external state, and is safe to expose from the cloud MCP. When the knowledge base is available the scaffold is auto-enriched with real metadata (existing field EDTs, related-table validation, auto-detected grid fields, etc.). Supported aotType values ---------------------------------------------- AxTable . AxClass . AxForm . AxEnum AxEdt . AxView . AxQuery . AxDataEntityView AxMenuItemDisplay . AxMenuItemAction . AxMenuItemOutput AxSecurityPrivilege . AxSecurityDuty . AxSecurityRole . AxSecurityPolicy AxReport . AxFormExtension . AxTableExtension . AxClassExtension AxEnumExtension . AxEdtExtension . AxQueryExtension . AxWorkflow Common options (all types) -------------------------------------------- label - human-readable label shown in the UI developerDoc - developer documentation string (tables/classes) AxTable -------------------------------------------------------------------------- tableGroup - Main | WorksheetHeader | WorksheetLine | Transaction | Parameter | Group cacheLookup - None | Found | FoundAndEmpty | NotInTTS | EntireTable fields - csv "Name:Type[:EDT[:Label[:mandatory]]]" Types: String Int Int64 Real Date DateTime Enum Container Guid indexes - csv "IndexName:field1+field2[:unique|:alternatekey]" relations - csv "RelName:RelatedTable:Field:RelatedField" titleField1/2 - field names for the lookup title createdBy / modifiedBy - true | false (default true) generateFind / generateExist / generateValidateWrite / generateInitValue - true | false AxClass -------------------------------------------------------------------------- extends - base class name implements - csv of interfaces abstract / final - true | false pattern - SysOperation | RunBase | Service | EventHandler | none (default) methods - csv of additional method names to stub AxForm ---------------------------------------------------------------------------- dataSourceTable - primary data-source table name pattern - SimpleList | DetailsTransaction | DetailsMaster | ListPage | Dialog gridFields - csv of field names for the grid (auto-detected from KB if omitted) detailFields - csv of field names for the detail group methods - csv of form method names to stub AxEnum ---------------------------------------------------------------------------- style - Ordinary | ComboBox | Radio | CheckBox values - csv "Name[:Label[:intValue]]" e.g. "Draft:Draft:0,Posted:Posted:1" AxEdt ----------------------------------------------------------------------------- extends - base EDT (e.g. Name, Description, Amount) stringSize - integer enumType - base enum for enum EDTs referenceTable - table that provides the lookup AxView ---------------------------------------------------------------------------- dataSources - csv "Table[:Alias]" fields - csv "DataSource.Field[:Alias]" AxQuery ------------------------------------------------------------------------- dataSources - csv "Table[:Alias[:JoinMode]]" AxDataEntityView ----------------------------------------------------------- primaryTable - root table joinTables - csv of additional tables publicEntityName - OData collection name (pluralised entity name) isPublic - true | false (default true) fields - csv "Table.Field[:PublicName]" (auto-detected from KB if omitted) AxMenuItemDisplay / Action / Output ------------------------------------------ objectName - target form / class / report name runOn - Server | Client | Called from (default Server) helpText - tooltip string AxSecurityPrivilege ----------------------------------------------------------- entryPoints - csv "MenuItemName[:ObjectType[:Grant]]" ObjectType: MenuItemDisplay | MenuItemAction | MenuItemOutput Grant: NoAccess | Read | Update | Create | Delete AxSecurityDuty --------------------------------------------------------------------- privileges - csv of privilege names to include AxSecurityRole ---------------------------------------------------------------------- duties - csv of duty names privileges - csv of privilege names (direct assignment -- avoid if possible) AxReport ------------------------------------------------------------------------- query- AxQuery name driving the dataset dataSourceTable - alternative: direct table name (if no query) AxFormExtension ----------------------------------------------------------------- baseForm - name of the standard form to extend fields - csv "ControlName[:EDT]" AxTableExtension --------------------------------------------------------------------- baseTable - name of the standard table to extend fields - csv "Name:Type[:EDT[:Label[:mandatory]]]" indexes - csv "IndexName:field1+field2[:unique|:alternatekey]" AxClassExtension (Chain of Command) ------------------------------------------- baseClass - name of the standard class/table/form to wrap baseType - class | table | form (default class) methods - csv of method names to wrap with CoC AxEnumExtension --------------------------------------------------------------- baseEnum - name of the standard enum to extend values - csv "Name[:Label[:intValue]]" IMPORTANT: start from value 10 or higher to avoid conflicts with standard values (D365 extension contract) AxEdtExtension ---------------------------------------------------------------- baseEdt - name of the standard EDT to extend stringSize - new StringSize (must be <= base EDT limit; omit to inherit) label - override label for this extension AxQueryExtension -------------------------------------------------------------- baseQuery - name of the standard query to extend dataSources - csv "Table[:Alias[:JoinMode]]" (add extra data sources) ranges - csv "DataSource.Field:value" (add filter ranges) AxSecurityPolicy (Row Level Security) ---------------------------------------- constrainedTable - primary table this policy restricts query - AxQuery name that defines the allowed rows operation - Select | Update | Create | Delete | Insert (default Select) enabled - true | false (default true) AxWorkflow --------------------------------------------------------------------- category - category name (links to a module/table) documentTable - table the workflow operates on documentMenuItem - menu item that opens the record

create_aot_object input schema
{
  "type": "object",
  "required": [
    "aotType",
    "name"
  ],
  "properties": {
    "name": {
      "type": "string",
      "description": "Object name -- must start with the ISV prefix, e.g. 'ALMMyTable', 'ABCSalesService'."
    },
    "aotType": {
      "type": "string",
      "description": "AOT type, e.g. 'AxTable', 'AxClass', 'AxForm', 'AxEnum', 'AxEdt', 'AxSecurityPrivilege', etc."
    },
    "options": {
      "type": [
        "object",
        "null"
      ],
      "default": null,
      "description": "Key-value options dict. Provide only the keys relevant to your aotType.\nExample for AxTable:\n{\n  \"label\":      \"My Table\",\n  \"tableGroup\":     \"Main\",\n  \"fields\":         \"MyId:String:MyIdEdt:My ID:mandatory,Description:String::Description\",\n  \"indexes\":      \"MyIdIdx:MyId:alternatekey\",\n  \"relations\": \"CustTable:CustTable:CustAccount:AccountNum\",\n  \"titleField1\":    \"MyId\",\n  \"generateFind\":   \"true\",\n  \"generateExist\":  \"true\",\n  \"generateValidateWrite\": \"true\"\n}",
      "additionalProperties": {
        "type": [
          "string",
          "null"
        ]
      }
    }
  }
}
create_aot_object annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
d365fo_set_connection
Advertised by the server during the last successful anonymous check.

Securely register the D365 F&O connection (URL + Entra app-registration credentials) for the CURRENT session. The client secret is encrypted in memory (AES-256-GCM), never written to disk and never echoed back. Once set, all odata_* and dmf_* tools use it automatically until it expires or you call d365fo_clear_connection. Use this instead of resending the secret on every call. In a locked server deployment (D365FO_LOCK_SERVER_CONFIG=true) this tool is disabled and the server's own environment credentials are used instead.

d365fo_set_connection input schema
{
  "type": "object",
  "required": [
    "baseUrl",
    "tenantId",
    "clientId",
    "clientSecret"
  ],
  "properties": {
    "baseUrl": {
      "type": "string",
      "description": "F&O environment base URL, e.g. https://yourenv.cloudax.dynamics.com (https only)."
    },
    "clientId": {
      "type": "string",
      "description": "App registration (client) id (GUID)."
    },
    "tenantId": {
      "type": "string",
      "description": "Entra (Azure AD) tenant id (GUID)."
    },
    "ttlMinutes": {
      "type": "integer",
      "default": 60,
      "description": "Minutes the connection stays cached for this session (1-480). Default 60."
    },
    "clientSecret": {
      "type": "string",
      "description": "App registration client secret. Encrypted in memory; never logged or persisted."
    }
  }
}
d365fo_set_connection annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
appinsights_clear_connection
Advertised by the server during the last successful anonymous check.

Removes the Application Insights connection cached for the current session by appinsights_set_connection.

appinsights_clear_connection input schema
{
  "type": "object",
  "properties": {}
}
appinsights_clear_connection annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
generate_data_entity
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer needs to CREATE a data entity (AxDataEntityView) AOT XML from a table for OData/DMF/data migration. Triggers: 'create data entity', 'generate entity', 'créer une data entity', 'exposer table via OData', 'DMF entity', 'entity for OData', 'entité de données', 'générer entity XML', 'AxDataEntityView pour', 'data entity from table'. Produces complete AxDataEntityView AOT XML with data sources, field mappings, entity key, IsPublic/PublicEntityName for OData, staging table template. Uses real field names and relations from the local custom model. ALWAYS call find_entity_for_table first to verify a standard entity doesn't already exist.

generate_data_entity input schema
{
  "type": "object",
  "required": [
    "tableName",
    "entityName"
  ],
  "properties": {
    "fields": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: specific fields to include (comma-separated). All fields if not specified."
    },
    "isPublic": {
      "type": "boolean",
      "default": true,
      "description": "Whether the entity should be available via OData (default: true)"
    },
    "tableName": {
      "type": "string",
      "description": "Primary table name, e.g. 'SalesTable', 'CustTable'"
    },
    "entityName": {
      "type": "string",
      "description": "Desired entity name, e.g. 'ALMSalesOrderEntity'"
    },
    "joinTables": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: additional tables to join (comma-separated), e.g. 'CustTable,InventDim'"
    },
    "publicEntityName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: OData collection name (e.g. 'SalesOrders'). Auto-generated if not provided."
    }
  }
}
generate_data_entity annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
find_event_handlers
Advertised by the server during the last successful anonymous check.
Advertised read-only

Find all event handlers that subscribe to events on a D365 table or class. Searches the knowledge base for [SubscribesTo(classStr(objectName), ...)] / [SubscribesTo(tableStr(objectName), ...)] attribute patterns. Use eventName to narrow results to a specific event (e.g. 'OnInserted'). WHEN: 'who handles X', 'what subscribes to Y', 'event handlers for Z', 'qui écoute l'événement', 'abonnés à l'événement', 'SubscribesTo', 'data events for'.

find_event_handlers input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "eventName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter to a specific event name, e.g. 'OnInserted', 'OnUpdated', 'OnDeleted', 'OnValidatingWrite'."
    },
    "objectName": {
      "type": "string",
      "description": "Table or class name to find event handlers for, e.g. 'SalesTable', 'PurchTable'."
    },
    "handlerType": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by handler type — 'DataEventHandler' or 'PrePostEventHandler'."
    }
  }
}
find_event_handlers annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
generate_xpp_form
Advertised by the server during the last successful anonymous check.
Advertised read-only

Generate a complete, compilable AxForm AOT XML with the CORRECT control serialization (<AxFormControl xmlns="" i:type="...">) for the requested pattern. Patterns: SimpleList, DetailsMaster, DetailsTransaction, ListPage, Dialog, DropDialog, Workspace, Extension. After generation, call validate_form_pattern on the result before write_aot_object.

generate_xpp_form input schema
{
  "type": "object",
  "required": [
    "formPattern",
    "formName"
  ],
  "properties": {
    "fields": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Comma-separated field names to include in the grid/header, e.g. 'AccountNum,Name,Status'"
    },
    "formName": {
      "type": "string",
      "description": "PascalCase form name, e.g. 'ALMCustomerForm'"
    },
    "modelName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Model name for label prefix, e.g. 'ALMMyModel'"
    },
    "fieldTypes": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: comma-separated control type per field (aligned 1:1 with fields). Values: String, Int, Real, Date, DateTime, Enum, CheckBox, Reference. If omitted, all fields default to AxFormStringControl (current behavior)."
    },
    "formPattern": {
      "type": "string",
      "description": "Form pattern: 'SimpleList', 'DetailsMaster', 'DetailsTransaction', 'ListPage', 'Dialog', 'DropDialog', 'Workspace', 'Extension'"
    },
    "primaryTable": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Primary/header table name, e.g. 'ALMCustomerTable'"
    },
    "secondaryTable": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Secondary/joined table name (DetailsTransaction: lines table; ListPage: InnerJoin lookup table), e.g. 'SalesTable'"
    }
  }
}
generate_xpp_form annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
appinsights_query
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Run a raw KQL (Kusto) query against the D365FO environment's Application Insights / Log Analytics workspace (read-only -- the query language has no mutation operators). Requires the connection to be configured first via appinsights_set_connection (or server env vars). Use the standard App Insights schema: requests, dependencies, exceptions, traces, customEvents, pageViews, performanceCounters. Prefer appinsights_diagnose_slowness for a ready-made "why is it slow" report -- use this tool for anything more specific/custom. Triggers: 'run this KQL', 'query app insights', 'requête KQL', 'log analytics query', 'custom App Insights query for my environment'.

appinsights_query input schema
{
  "type": "object",
  "required": [
    "kql"
  ],
  "properties": {
    "kql": {
      "type": "string",
      "description": "KQL query text, e.g. \"exceptions | where timestamp > ago(1h) | take 20\". Do NOT include an explicit 'ago()'/time-range filter for the primary time column -- use the lookbackHours parameter instead (applied as the query time range)."
    },
    "maxRows": {
      "type": "integer",
      "default": 100,
      "description": "Max rows to return (1-500). Default 100."
    },
    "lookbackHours": {
      "type": "integer",
      "default": 24,
      "description": "How far back to query, in hours (1-720). Default 24."
    }
  }
}
appinsights_query annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
search_context_docs
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: the user asks about business/functional context that lives OUTSIDE the D365 code KB -- specs, functional design docs, mapping sheets, contracts, meeting notes, screenshots' captions -- anything an admin uploaded via the admin portal's 'Context Documents' library (PDF, Word .docx, Excel .xlsx/.xlsm, CSV, plain text/Markdown/JSON). Does NOT search X++ code or AOT objects -- use search_d365_code / get_object_details for that. Triggers: 'what does the spec say about...', 'check the mapping document for...', 'cherche dans les documents de contexte', 'according to the functional design'. An excerpt containing a 'Image N' marker has a picture the text cannot convey (a diagram, a screenshot): call again with includeImages=true to receive those pictures inline.

search_context_docs input schema
{
  "type": "object",
  "required": [
    "query"
  ],
  "properties": {
    "query": {
      "type": "string",
      "description": "Natural-language search query."
    },
    "maxResults": {
      "type": "integer",
      "default": 8,
      "description": "Max distinct documents to return (1-20). Default 8."
    },
    "includeImages": {
      "type": "boolean",
      "default": false,
      "description": "Attach the images embedded in the matched documents (Word only). Off by default because each picture is inlined as base64 and is far larger than the text around it."
    }
  }
}
search_context_docs annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
trace_security_chain
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: security audit -- need the TECHNICAL chain from Role/Duty/Privilege to Entry Points and Table/Form permissions. Also handles BUSINESS-LANGUAGE role explanation when businessLanguage=true. Triggers (technical): 'sécurité de', 'who can access', 'security for', 'role duty privilege', 'droits sur', 'technical security chain', 'trace le rôle', 'what privileges does', 'what duties are assigned', 'which role allows', 'accès au formulaire', 'what roles have access', 'quel rôle donne accès'. Triggers (business language): 'what can a user with role X do', 'explain this role', 'what does this role give access to', 'quel accès donne ce rôle', 'droits du rôle', 'what licence does this role need', 'droits requis pour'. Traverses: Role -> Duties -> Privileges -> Entry Points -> Table/Form Permissions. Set businessLanguage=true for plain-language capability list (no Duty/Privilege IDs). NOT for licence cost inference per entry point -- use trace_role_license_tree for that.

trace_security_chain input schema
{
  "type": "object",
  "required": [
    "securityObjectName"
  ],
  "properties": {
    "businessLanguage": {
      "type": "boolean",
      "default": false,
      "description": "When true, explains the role in plain business language (capabilities list) instead of the technical Role->Duty->Privilege chain. Default: false."
    },
    "securityObjectName": {
      "type": "string",
      "description": "Security object name, e.g. 'SystemAdministrator', 'VendInvoiceApprovalConfig'"
    }
  }
}
trace_security_chain annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
find_relation_path
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: you need to know HOW two AOT objects are connected -- the chain of relations linking them. Triggers: 'how is X related to Y', 'comment X est lié à Y', 'path between', 'chemin entre', 'lien entre deux tables', 'connection between', 'is X reachable from Y'. Walks the pre-computed relation graph (FK, DeleteAction, DataSource, Extension, Security edges -- both directions) and returns the SHORTEST navigation path(s) as a chain of object names + edge kinds. Token-light: returns ONLY names and relation kinds, never full object source. O(1)-per-node BFS, no vector scan. Use `get_relation_graph` for the full neighbourhood of a single object; use this to traverse multiple hops between two known objects.

find_relation_path input schema
{
  "type": "object",
  "required": [
    "source",
    "target"
  ],
  "properties": {
    "source": {
      "type": "string",
      "description": "Start object name, e.g. 'SalesTable'"
    },
    "target": {
      "type": "string",
      "description": "Destination object name, e.g. 'CustTable'"
    },
    "maxDepth": {
      "type": "integer",
      "default": 4,
      "description": "Maximum hops to traverse (default: 4, max: 8). Higher = slower, more paths."
    },
    "maxPaths": {
      "type": "integer",
      "default": 5,
      "description": "Maximum number of distinct paths to return (default: 5, max: 20)."
    }
  }
}
find_relation_path annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_estimate_effort
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

WHEN: user asks for an effort estimate, chiffrage, or development hours for a D365 Work Item. [~] PRIORITY TRIGGER: call AFTER `ado_analyze_workitem` when the user asks 'how long?', 'estimate this', 'chiffre ce WI'. [t] EFFORT ESTIMATOR -- Estimate D365 F&O development effort for a Work Item. Uses KB signals (object count, method complexity, existing extensions, relation depth) combined with ADO history (similar past tasks) to produce a structured hour estimate broken down by phase: Analysis / Dev / Test / Deploy. Returns: * Per-phase hour breakdown (table) * KB signals used (objects found, extensions, complexity flags) * Confidence level and risk factors * Ready-to-paste estimate for ADO task Original Estimate field Triggers: 'estimate WI #N', 'how long for WI #N', 'chiffrage WI #N', 'effort estimate', 'combien de jours pour', 'combien d\'heures pour'.

ado_estimate_effort input schema
{
  "type": "object",
  "required": [
    "workItemId"
  ],
  "properties": {
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "workItemId": {
      "type": "integer",
      "description": "Work Item ID to estimate, e.g. 6587."
    },
    "focusObjects": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: comma-separated D365 object names to force-include in analysis (speeds up estimation)."
    }
  }
}
ado_estimate_effort annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_create_test_plan
Advertised by the server during the last successful anonymous check.
Open-world

WHEN: the user wants the manual test cases of a feature (a User Story, Feature or Requirement work item) created in Azure DevOps Test Plans: a test plan, a suite tied to that work item, and one Test Case per scenario with its steps. Triggers: 'create the test plan for WI N', 'créer le plan de test', 'put these tests in Azure Test Plans', 'cas de test dans DevOps'. YOU write the test cases: read the work item (ado_analyze_workitem) and, when useful, the code, then pass them as testCasesJson, a JSON array [{"title":"...","priority":1-4,"tags":["..."],"steps":[{"action":"...","expected":"..."}]}]. Each case needs a title and at least one step; every step needs an action, and an expected result for the steps that verify something. Without testCasesJson the tool falls back to one generic case per acceptance-criteria line, which is much weaker. SAFE BY DEFAULT: with confirm=false (default) nothing is written; it returns exactly what would be created and what already exists. Show that preview to the user, and only call again with confirm=true after they approved it. Test cases whose title already exists on the work item are skipped, so running it twice does not duplicate. Needs a PAT with Work Items (Read & write) and Test Management (Read & write); creating plans and suites also needs the Basic + Test Plans access level.

ado_create_test_plan input schema
{
  "type": "object",
  "required": [
    "workItemId"
  ],
  "properties": {
    "planId": {
      "type": [
        "integer",
        "null"
      ],
      "default": null,
      "description": "Use this existing test plan instead of looking one up by name."
    },
    "confirm": {
      "type": "boolean",
      "default": false,
      "description": "false (default): preview only, nothing is written. true: create. Only after the user approved the preview."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Azure DevOps project. Falls back to DEVOPS_PROJECT."
    },
    "planName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Name of the test plan. Default: 'Tests - WI {id} {title}'. An existing plan with this name is reused."
    },
    "workItemId": {
      "type": "integer",
      "description": "Work item the tests verify (User Story, Feature, Requirement...), e.g. 6587."
    },
    "testCasesJson": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "JSON array of test cases written from the acceptance criteria. Strongly recommended. See the tool description for the shape."
    }
  }
}
ado_create_test_plan annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
ado_list_prs
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

[~] PRIORITY TRIGGER: Use this tool when user mentions 'PR', 'Pull Request', 'list PRs', 'show PRs', 'active PRs', 'mes PR', 'liste des PR', 'pull requests ouverts', 'what PRs are open', 'PRs by [author]', 'PRs targeting [branch]'. NEVER call search_d365_code for PR listing requests. List Pull Requests in an Azure DevOps Git repository. If `repositoryId` is unknown, omit it and all repositories will be listed first. Filters: status (Active/Completed/Abandoned/All), author display name, target branch. Returns: PR ID, title, author, source->target branch, review status, linked work items, creation date. Use `ado_analyze_pr_impact` with a PR ID to get full D365 code impact analysis. Requires DEVOPS_ORG_URL + DEVOPS_PAT (Code: Read scope).

ado_list_prs input schema
{
  "type": "object",
  "properties": {
    "top": {
      "type": "integer",
      "default": 25,
      "description": "Max results to return (1-100, default 25)."
    },
    "author": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by author display name (partial match), e.g. 'Alim'."
    },
    "status": {
      "type": "string",
      "default": "Active",
      "description": "PR status filter: Active (default), Completed, Abandoned, All."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "repositoryId": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Git repository name or ID. If not known, leave empty to list all repos first."
    },
    "targetBranch": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by target branch, e.g. 'main', 'develop'."
    }
  }
}
ado_list_prs annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
validate_object_naming
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer needs to check that a proposed object name follows D365 + ISV naming conventions, is unique against the indexed KB, and does not collide with a reserved or standard prefix. Triggers: 'is this a valid name', 'check naming', 'name conflict', 'valider le nommage'. Cloud-safe: KB read only, no writes.

validate_object_naming input schema
{
  "type": "object",
  "required": [
    "proposedName",
    "aotType",
    "isvPrefix"
  ],
  "properties": {
    "aotType": {
      "type": "string",
      "description": "AOT type the name will live in: AxClass, AxTable, AxForm, AxEdt, AxEnum, AxTableExtension, AxFormExtension, AxClassExtension, etc."
    },
    "isvPrefix": {
      "type": "string",
      "description": "ISV prefix you must use, e.g. 'ALM'. Required for all custom objects."
    },
    "proposedName": {
      "type": "string",
      "description": "Proposed name, e.g. 'ALMSalesLine.Extension', 'ALMCustomerTable', 'SalesLineALM_Extension'."
    }
  }
}
validate_object_naming annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
generate_diagram
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: generating a visual diagram of D365 table relationships or security chains. Triggers: 'generate diagram', 'diagramme', 'visualize', 'schéma', 'ER diagram', 'entity-relationship', 'relation diagram', 'security diagram', 'show connections'. Generate visual Mermaid diagrams from D365 F&O knowledge base data. Diagrams render directly in Copilot Chat, Cursor, Claude, and markdown viewers. Types: 'er' (entity-relationship diagram for a table and its relations), 'security' (security chain: Role->Duty->Privilege->EntryPoints -- use when you need a VISUAL Mermaid diagram; for the structured text chain with tables of duties/privileges/entry-points use `trace_security_chain` instead). Note: 'flow' (execution flowchart) is disabled -- static call trees are misleading in D365 due to CoC and event handlers.

generate_diagram input schema
{
  "type": "object",
  "required": [
    "diagramType",
    "objectName"
  ],
  "properties": {
    "maxRelated": {
      "type": "integer",
      "default": 12,
      "description": "Optional: maximum related entities to show (default: 12, max: 20)"
    },
    "methodName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Not used -- flow diagrams are disabled."
    },
    "objectName": {
      "type": "string",
      "description": "Object name to diagram, e.g. 'SalesTable', 'SystemAdministrator'"
    },
    "diagramType": {
      "type": "string",
      "description": "Diagram type: 'er' (entity-relationship) or 'security' (security chain). 'flow' is disabled."
    }
  }
}
generate_diagram annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
save_client_profile
Advertised by the server during the last successful anonymous check.
Advertised destructive

WHEN: no profile was found by resolve_client_profile and the user wants one created (or updated) for their client, so future release-note requests never need v1/v2/customModelIds again. Creates or updates a Client Profile keyed by the CURRENT caller's ADO org/project (auto-detected from MCP headers -- not a parameter). Only the fields you pass are changed; omit a field to leave it untouched on an existing profile.

save_client_profile input schema
{
  "type": "object",
  "required": [
    "name"
  ],
  "properties": {
    "name": {
      "type": "string",
      "description": "Friendly client name, e.g. \"Acme Corp\"."
    },
    "customModelIds": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Comma-separated custom model id(s) from the Custom Models tab -- the client's own extensions AND any separate ISV vendor models."
    },
    "targetVersionTag": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "The version being evaluated for upgrade, e.g. \"10.0.2645.32\"."
    },
    "currentVersionTag": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "The client's current live D365FO version tag, e.g. \"10.0.2527.109\"."
    }
  }
}
save_client_profile annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": true
}
dmf_export_package
Advertised by the server during the last successful anonymous check.
Open-world

Trigger a bulk export through the Data Management package REST API and return the download URL when complete. NOTE: the DMF EXPORT project (definitionGroupId) must already exist in FO with its entities + mapping configured -- create it with dmf_create_data_project if needed. For ad-hoc exports without a project, use odata_export_entity instead.

dmf_export_package input schema
{
  "type": "object",
  "required": [
    "definitionGroupId",
    "legalEntity"
  ],
  "properties": {
    "reExecute": {
      "type": "boolean",
      "default": true,
      "description": "Re-execute the project (true) or reuse last staging (false). Default true."
    },
    "legalEntity": {
      "type": "string",
      "description": "Legal entity / company id to export from, e.g. 'USMF'."
    },
    "packageName": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional package name. Default derived from the project id."
    },
    "definitionGroupId": {
      "type": "string",
      "description": "Existing DMF export project (definitionGroupId) in FO."
    },
    "pollTimeoutSeconds": {
      "type": "integer",
      "default": 300,
      "description": "Max seconds to poll for completion. Default 300."
    }
  }
}
dmf_export_package annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_post_pr_comment
Advertised by the server during the last successful anonymous check.
Open-world

WHEN: user explicitly asks to post, add, or save a review comment to an ADO Pull Request. [~] PRIORITY TRIGGER: call AFTER `ado_analyze_pr_impact` when user says 'post the review', 'add comment to PR', 'ajoute le commentaire sur la PR'. WARNING: ALWAYS ask for explicit user confirmation before calling this tool. Recommended workflow: (1) call `ado_analyze_pr_impact`, (2) show the result, (3) ask 'Shall I post this review comment to PR #X?', (4) only then call this tool. Requires DEVOPS_ORG_URL + DEVOPS_PAT (Pull Request Threads: Read & Write).

ado_post_pr_comment input schema
{
  "type": "object",
  "required": [
    "repositoryId",
    "prId",
    "commentText"
  ],
  "properties": {
    "prId": {
      "type": "integer",
      "description": "Pull Request ID (integer), e.g. 42."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: Azure DevOps project name. Falls back to DEVOPS_PROJECT env var."
    },
    "filePath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: file path for an inline comment. Leave empty for a top-level PR comment."
    },
    "lineNumber": {
      "type": [
        "integer",
        "null"
      ],
      "default": null,
      "description": "Optional: line number for an inline comment (used with filePath)."
    },
    "commentText": {
      "type": "string",
      "description": "Markdown text to post. Use the ready-to-post block from `ado_analyze_pr_impact`."
    },
    "repositoryId": {
      "type": "string",
      "description": "Git repository name or ID."
    }
  }
}
ado_post_pr_comment annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
list_release_note_inputs
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: ALWAYS call this FIRST, before prepare_release_note_context -- it discovers the exact D365FO version strings and custom model ids actually indexed on THIS server, which you cannot guess. Triggers: 'release note', 'compare D365 versions', 'upgrade impact for a client', 'what changed for this client', 'regression risk', 'note de version'. Returns every indexed version (exact 'version' string to use as v1/v2, e.g. 10.0.2527.109) and every ready-to-use custom model (exact 'id' to use as customModelIds, plus its name and optional clientGroup tag -- models sharing the same clientGroup belong to the same client and should usually ALL be passed together, e.g. the client's own extensions AND a separate ISV vendor model). IMPORTANT: this server holds the ACTUAL indexed code diff and the client's ACTUAL custom code -- generic Microsoft Learn / Azure Updates / documentation-search tools do NOT have this data and must NOT be used for D365 F&O release-note or upgrade-impact questions about a specific client; those tools only know public, generic release notes, not this client's real regression/opportunity picture.

list_release_note_inputs input schema
{
  "type": "object",
  "properties": {}
}
list_release_note_inputs annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
federated_search
Advertised by the server during the last successful anonymous check.
Advertised read-only

Fan-out search across multiple ALM XPP MCP server instances in parallel and merge results using Reciprocal Rank Fusion (RRF). Useful when D365 code is split across multiple organisations or knowledge bases (e.g. one per client, one standard KB). Peer servers are configured via D365_FEDERATION_PEERS env var (comma-separated list of base URLs, e.g. https://org2.almxpp.com). The local server is always included as the primary source.

federated_search input schema
{
  "type": "object",
  "required": [
    "query"
  ],
  "properties": {
    "topK": {
      "type": "integer",
      "default": 5,
      "description": "Number of results to return per server (default 5)"
    },
    "query": {
      "type": "string",
      "description": "Natural language search query"
    },
    "peerApiKeys": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional MCP API keys for peer servers, comma-separated (same order as D365_FEDERATION_PEERS)"
    }
  }
}
federated_search annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
search_labels
Advertised by the server during the last successful anonymous check.
Advertised read-only

Search D365 F&O labels across all indexed languages. Given text (e.g. 'Sales order'), finds the matching label ID (@SYS12345). Given a label ID (e.g. '@SYS12345' or '@SYS:12345'), finds the text in all languages. Accepts both D365 short form (@SYS124480) and colon form (@SYS:124480) -- both are normalized automatically. Searches across 392K+ label entries. WORKFLOW: call search_labels first to resolve the label text, then call find_references with the same label ID to find ALL X++ objects (forms, tables, classes, reports) that use it in their code or metadata. Languages: en-US and fr are loaded at startup. Other languages (de, nl, ar, es, zh...) are loaded on-demand -- first call ~15s, then instant.

search_labels input schema
{
  "type": "object",
  "required": [
    "query"
  ],
  "properties": {
    "query": {
      "type": "string",
      "description": "Text to search for (e.g. 'Sales order', 'Invoice amount') or a label ID (e.g. '@SYS12345', '@AccountsReceivable:CustInvoice')"
    },
    "language": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: filter by language code (e.g. 'en-US', 'fr', 'de', 'nl'). Leave empty for all languages."
    },
    "maxResults": {
      "type": "integer",
      "default": 20,
      "description": "Maximum results (default: 20, max: 50)"
    }
  }
}
search_labels annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
odata_export_entity
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Export any D365 F&O data entity via OData (transactional, no DMF project required). Universal: works for ANY public entity. Supports $select, $filter, $orderby and follows server paging automatically. Returns CSV (default) or JSON. Use for live/ad-hoc exports and small-to-medium volumes. For very large bulk exports prefer dmf_export_package. Resolve the entity set name from the KB first (find_entity_for_table / get_data_entity_info) -- do not invent entity names.

odata_export_entity input schema
{
  "type": "object",
  "required": [
    "entitySet"
  ],
  "properties": {
    "filter": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional OData $filter expression."
    },
    "format": {
      "type": "string",
      "default": "csv",
      "description": "Output format: 'csv' (default) or 'json'."
    },
    "select": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional $select (comma-separated fields). Empty = all fields."
    },
    "maxRows": {
      "type": "integer",
      "default": 1000,
      "description": "Max rows to return (0/empty = no cap, follows all pages). Default 1000."
    },
    "orderBy": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional $orderby expression."
    },
    "entitySet": {
      "type": "string",
      "description": "OData public entity set name, e.g. 'CustomersV3', 'ReleasedProductsV2'."
    },
    "outputPath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional file path to also write the full result to (e.g. C:\\temp\\export.csv)."
    },
    "crossCompany": {
      "type": "boolean",
      "default": false,
      "description": "Set true to query across all legal entities (adds cross-company=true)."
    }
  }
}
odata_export_entity annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
ado_record_test_result
Advertised by the server during the last successful anonymous check.
Open-world

WHEN: a manual test case of an Azure DevOps test plan has been run (by you through the ERP tools, or reported by the user) and its result must be recorded, so the plan shows what passed and what failed. Triggers: 'record the result', 'mark the test passed', 'enregistre le résultat du test', 'set the test outcome'. Needs the plan id and suite id (the ones ado_create_test_plan returned) and the test case id. outcome is Passed, Failed, Blocked or NotApplicable. Pass `evidence` (what was done and what was observed, no personal data): it is added as a comment on the test case, which is the audit trail. SAFE BY DEFAULT: confirm=false only shows what would be recorded. Call again with confirm=true after the user agreed. Never record Passed for something that was not verified. Needs a PAT with Test Management (Read & write) and Work Items (Read & write).

ado_record_test_result input schema
{
  "type": "object",
  "required": [
    "planId",
    "suiteId",
    "testCaseId",
    "outcome"
  ],
  "properties": {
    "planId": {
      "type": "integer",
      "description": "Test plan id."
    },
    "confirm": {
      "type": "boolean",
      "default": false,
      "description": "false (default): preview only. true: record."
    },
    "outcome": {
      "type": "string",
      "description": "Passed, Failed, Blocked or NotApplicable."
    },
    "project": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Azure DevOps project. Falls back to DEVOPS_PROJECT."
    },
    "suiteId": {
      "type": "integer",
      "description": "Test suite id inside the plan."
    },
    "evidence": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "What was done and what was observed. Added as a comment on the test case."
    },
    "testCaseId": {
      "type": "integer",
      "description": "Id of the Test Case work item."
    }
  }
}
ado_record_test_result annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
list_custom_model_objects
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer wants to see what custom/extension objects exist in their model. Triggers: 'list my custom objects', 'what have we customized', 'show ISV objects', 'list custom model', 'what objects are in our model'. List all D365 F&O objects in the custom/extension model directory on disk. Reads the file system directly -- always reflects the latest uncommitted state. Pass `customModelPath` to specify a model directory; or set it once via the `D365-Custom-Model-Path` header in your .mcp.json (applies to all tool calls automatically).

list_custom_model_objects input schema
{
  "type": "object",
  "properties": {
    "customModelPath": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: path to the custom model directory (e.g. 'C:\\\\AOTExport\\\\MyModel'). Overrides the header and server-configured path."
    }
  }
}
list_custom_model_objects annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
analyze_upgrade_impact
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: upgrading D365 F&O to a new version or applying a Microsoft update -- check if your custom code will break. Triggers: 'upgrade D365', 'mise à niveau', 'will this break after upgrade', 'compatibilité après upgrade', 'impact de la mise à jour', 'check CoC targets after update'. Analyze upgrade risk for your custom D365 F&O model. Cross-references EVERY Chain of Command target, event handler hook, table/form/class extension, and hard-coded object reference in your custom model against the standard indexed codebase. Detects: removed objects, changed method signatures, deprecated APIs (RunBase, Dialog, WinAPI, COM), [Hookable(false)] and [Wrappable(false)] extensibility blocks, renamed fields, and internal methods. Returns a prioritized risk report with fix recommendations. Requires D365_CUSTOM_MODEL_PATH.

analyze_upgrade_impact input schema
{
  "type": "object",
  "properties": {}
}
analyze_upgrade_impact annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
fix_best_practice_violations
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: validate_best_practices returned violations and you want concrete X++ fix suggestions. Returns each BP violation with its built-in fix suggestion AND (when Ollama is available) an AI-generated X++ code patch using the source from the knowledge base. Triggers: 'fix best practice', 'corriger les violations', 'generate fix', 'patch BP'. Set generatePatches=true to call Ollama (ALMXPP_SUMMARIZE_MODEL) for code patches -- requires Ollama running at OLLAMA_HOST. Falls back to built-in fix suggestions if unavailable.

fix_best_practice_violations input schema
{
  "type": "object",
  "required": [
    "objectName"
  ],
  "properties": {
    "objectName": {
      "type": "string",
      "description": "Object or method to fix, e.g. 'SalesLine' or 'SalesLine.insert'."
    },
    "maxViolations": {
      "type": "integer",
      "default": 10,
      "description": "Max violations to process (1-20, default 10)."
    },
    "generatePatches": {
      "type": "boolean",
      "default": false,
      "description": "Generate X++ code patches via Ollama (requires OLLAMA_HOST). Default false."
    }
  }
}
fix_best_practice_violations annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
erp_call
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

WHEN: to run one ERP MCP tool on ONE Dynamics 365 F&O instance, as the signed-in user (their own F&O roles and legal entities apply). Get the tool name and argument schema from erp_list_tools first. On protected instances (PROD) any tool that is not flagged read-only needs confirm=true, and you must only set it after the user explicitly approved the exact action. On a readOnly instance every tool that can change data is refused and confirm does not override it. To compare two instances, call erp_call once per instance with the same read-only tool and diff the results.

erp_call input schema
{
  "type": "object",
  "required": [
    "instance",
    "tool"
  ],
  "properties": {
    "tool": {
      "type": "string",
      "description": "Exact tool name from erp_list_tools."
    },
    "confirm": {
      "type": "boolean",
      "default": false,
      "description": "Required true to run a non read-only tool on a protected instance."
    },
    "instance": {
      "type": "string",
      "description": "Instance alias from erp_list_instances."
    },
    "arguments": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "JSON object with the tool arguments, as a string. Omit or \"{}\" when none."
    }
  }
}
erp_call annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": true
}
generate_release_note_document
Advertised by the server during the last successful anonymous check.

WHEN: you have already called prepare_release_note_context and analyzed its 'objects' array yourself, producing a findings JSON array per the 'instructions' field it returned. This tool renders that findings array into a downloadable Word (.docx, detailed appendix) and PowerPoint (.pptx, executive summary) release note and returns their download URLs. Does NOT call any LLM itself -- the reasoning must already be done by you.

generate_release_note_document input schema
{
  "type": "object",
  "required": [
    "v1",
    "v2",
    "customModelLabel",
    "findingsJson",
    "touchedAdded",
    "touchedRemoved",
    "touchedModified"
  ],
  "properties": {
    "v1": {
      "type": "string",
      "description": "Older D365FO version (same value passed to prepare_release_note_context)."
    },
    "v2": {
      "type": "string",
      "description": "Newer D365FO version (same value passed to prepare_release_note_context)."
    },
    "findingsJson": {
      "type": "string",
      "description": "JSON array of your findings, one per object from prepare_release_note_context's 'objects' array. Schema: [{\"aotType\":\"...\",\"objectName\":\"...\",\"changeType\":\"Added|Removed|Modified\",\"riskLevel\":\"Critical|Warning|Info|None\",\"whatChanged\":\"...\",\"documentedInMsLearn\":true|false,\"msLearnReference\":\"...\",\"undocumentedReason\":\"...\",\"regressionRisk\":\"...\",\"opportunity\":\"...\",\"recommendation\":\"...\"}]"
    },
    "touchedAdded": {
      "type": "integer",
      "description": "touchedAdded count returned by prepare_release_note_context."
    },
    "touchedRemoved": {
      "type": "integer",
      "description": "touchedRemoved count returned by prepare_release_note_context."
    },
    "businessContext": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional business/functional context (same value passed to prepare_release_note_context), included in the Word document."
    },
    "touchedModified": {
      "type": "integer",
      "description": "touchedModified count returned by prepare_release_note_context."
    },
    "customModelLabel": {
      "type": "string",
      "description": "Custom model label(s) -- use the customModelLabel field returned by prepare_release_note_context."
    }
  }
}
generate_release_note_document annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
get_menu_item_info
Advertised by the server during the last successful anonymous check.
Advertised read-only

WHEN: developer needs to resolve a menu item -- find its target object (form / action / output), linked security privilege, label, and parameters. Triggers: 'menu item info', 'what does menu item X open', 'menu item target', 'security privilege for menu item'. Cloud-safe: parses the AxMenuItem XML already loaded in the KB. Returns the resolved object name, menu item type (Display/Action/Output), linked privilege, and the form/class/report it points to.

get_menu_item_info input schema
{
  "type": "object",
  "required": [
    "menuItemName"
  ],
  "properties": {
    "menuItemName": {
      "type": "string",
      "description": "Menu item name (without prefix), e.g. 'CustTable', 'SalesTableListPage', 'ALMMyMenuItem'."
    },
    "menuItemType": {
      "type": [
        "string",
        "null"
      ],
      "default": null,
      "description": "Optional: menu item type to disambiguate when multiple exist with the same name. 'Display', 'Action', or 'Output'. Default: try all."
    }
  }
}
get_menu_item_info annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}

Prompts

4 advertised

xpp_functional_mode — D365 F&O Functional Analyst Mode

Advertised by the server during the last successful anonymous check.

Business-facing grounding for non-technical users: explain fields by label (not field name), methods and relations in business terms, never show X++ code unless explicitly asked. Use for gap-fit analysis, FDD authoring, and functional Q&A.

No arguments advertised.

xpp_expert_guidelines — D365 F&O X++ Expert Guidelines

Advertised by the server during the last successful anonymous check.

Core principles for context-driven X++ development: object-relationship analysis before code, security/performance/transaction best practices, testing (SysTest) requirements.

No arguments advertised.

xpp_system_instructions — D365 F&O X++ System Instructions

Advertised by the server during the last successful anonymous check.

Complete X++ grounding ruleset: mandatory context-before-code workflow, tool decision tree, CoC authoring rules, naming conventions, enum/form-extension XML structure, build/BP error triage. Load this at the start of any X++ development session on this server.

No arguments advertised.

xpp_form_patterns — D365 F&O AxForm Pattern Reference

Advertised by the server during the last successful anonymous check.

Exhaustive AOT form-control serialization rules: the AxFormControl polymorphic wrapper requirement, i:type control-type reference table. Load before generating or editing any AxForm / AxFormExtension XML.

No arguments advertised.

Resources

4 advertised

xpp-hallucination-guardrails

Advertised by the server during the last successful anonymous check.

Dense catalogue of X++ vs C#/Java mistranslation pitfalls (LINQ, async/await, table buffers, transactions, CoC next(), labels, security chain) -- read this before generating or explaining X++ code to avoid the most common hallucination patterns.

xpp-hallucination-guardrails URI
docs://xpp-hallucination-guardrails

MIME type: text/markdown

mcp-tools-reference

Advertised by the server during the last successful anonymous check.

Complete reference of every MCP tool exposed by this server (107+ tools across cloud/local scopes), grouped by category with descriptions.

mcp-tools-reference URI
docs://mcp-tools

MIME type: text/markdown

adr-index

Advertised by the server during the last successful anonymous check.

Index of Architecture Decision Records (ADRs) for this MCP server -- lists every major technical decision recorded so far.

adr-index URI
docs://adr

MIME type: text/markdown

xpp-solution-build-deploy

Advertised by the server during the last successful anonymous check.

How to create a Finance & Operations model/solution/project (ModelUtil.exe, .rnrproj/.sln, project-type GUID), build (xppc.exe, incremental vs full, stale-symbol), diagnose the #1 missing-model-reference build error, and deploy correctly depending on the environment family (PPAC/UDE vs LCS/CHE vs local) -- also clarifies that real F&O development happens in Visual Studio, not VS Code.

xpp-solution-build-deploy URI
docs://xpp-solution-build-deploy

MIME type: text/markdown

adr-by-id

Advertised by the server during the last successful anonymous check.

Read a single Architecture Decision Record by its id, e.g. '0001-cloud-local-mcp-split'. See docs://adr for the full list of ids.

adr-by-id URI template
docs://adr/{id}

MIME type: text/markdown

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
1457 ms
95th percentile latency
1457 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 1457 ms · metadata changed · 104 tool(s), 4 resource(s), 1 resource template(s), 4 prompt(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http alm-xpp-mcp-almxpp https://api.almxpp.com/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add alm-xpp-mcp-almxpp --url https://api.almxpp.com/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable alm-xpp-mcp-almxpp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "alm-xpp-mcp-almxpp": {
      "url": "https://api.almxpp.com/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "alm-xpp-mcp-almxpp": {
      "type": "streamableHttp",
      "url": "https://api.almxpp.com/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start alm-xpp-mcp-almxpp and inspect its tools.
VS Code configuration
{
  "servers": {
    "alm-xpp-mcp-almxpp": {
      "type": "http",
      "url": "https://api.almxpp.com/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http alm-xpp-mcp-almxpp https://api.almxpp.com/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://api.almxpp.com/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "alm-xpp-mcp-almxpp": {
      "serverUrl": "https://api.almxpp.com/mcp"
    }
  }
}

Let your agent find its tools

Find servers like ALM XPP MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.