d365fo-mcp 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +5 -4
  2. package/bridge/D365MetadataBridge/Protocol/RequestDispatcher.cs +96 -4
  3. package/bridge/D365MetadataBridge/Services/MetadataWriteService.cs +983 -180
  4. package/dist/bridge/bridgeAdapter.d.ts +41 -4
  5. package/dist/bridge/bridgeAdapter.js +106 -9
  6. package/dist/bridge/bridgeClient.d.ts +18 -3
  7. package/dist/bridge/bridgeClient.js +25 -5
  8. package/dist/bridge/index.d.ts +1 -1
  9. package/dist/bridge/index.js +1 -1
  10. package/dist/metadata/labelParser.js +8 -4
  11. package/dist/metadata/modelDescriptor.d.ts +53 -0
  12. package/dist/metadata/modelDescriptor.js +129 -0
  13. package/dist/metadata/symbolIndex.js +4 -6
  14. package/dist/metadata/types.d.ts +8 -0
  15. package/dist/metadata/xmlParser.d.ts +5 -1
  16. package/dist/metadata/xmlParser.js +10 -2
  17. package/dist/metadata/xppDeclaration.d.ts +22 -0
  18. package/dist/metadata/xppDeclaration.js +36 -2
  19. package/dist/scripts/build-database.js +14 -10
  20. package/dist/scripts/build-fts.js +14 -10
  21. package/dist/scripts/extract-metadata.js +17 -4
  22. package/dist/server/toolSchemas/d365foFile.js +18 -15
  23. package/dist/tools/buildProject.d.ts +10 -0
  24. package/dist/tools/buildProject.js +49 -38
  25. package/dist/tools/compileLabels.d.ts +62 -0
  26. package/dist/tools/compileLabels.js +239 -0
  27. package/dist/tools/createD365File.d.ts +4 -3
  28. package/dist/tools/createD365File.js +38 -10
  29. package/dist/tools/createdArtifactLedger.d.ts +5 -1
  30. package/dist/tools/createdArtifactLedger.js +0 -0
  31. package/dist/tools/d365foFileOpSpecs.js +42 -3
  32. package/dist/tools/dataEntityInfo.d.ts +9 -12
  33. package/dist/tools/dataEntityInfo.js +12 -3
  34. package/dist/tools/dataEntityViewExtensionXml.d.ts +9 -0
  35. package/dist/tools/dataEntityViewExtensionXml.js +24 -12
  36. package/dist/tools/dataEntityXml.d.ts +76 -10
  37. package/dist/tools/dataEntityXml.js +184 -35
  38. package/dist/tools/enumInfo.d.ts +10 -3
  39. package/dist/tools/enumInfo.js +69 -10
  40. package/dist/tools/formInfo.js +11 -0
  41. package/dist/tools/generateD365Xml.d.ts +157 -0
  42. package/dist/tools/generateD365Xml.js +30 -5
  43. package/dist/tools/modifyD365File.d.ts +19 -0
  44. package/dist/tools/modifyD365File.js +275 -5
  45. package/dist/tools/queryInfo.d.ts +10 -3
  46. package/dist/tools/queryInfo.js +123 -10
  47. package/dist/tools/reportInfo.js +42 -14
  48. package/dist/tools/resolveReferences.d.ts +14 -1
  49. package/dist/tools/resolveReferences.js +160 -14
  50. package/dist/tools/runBpCheck.js +18 -0
  51. package/dist/tools/updateSymbolIndex.js +75 -5
  52. package/dist/tools/validateCode.js +2 -1
  53. package/dist/tools/viewInfo.d.ts +15 -4
  54. package/dist/tools/viewInfo.js +162 -9
  55. package/dist/tools/xppKnowledge.js +55 -10
  56. package/dist/utils/axTablePropertyOrder.d.ts +1 -1
  57. package/dist/utils/axTablePropertyOrder.js +5 -0
  58. package/dist/utils/frameworkBin.d.ts +19 -0
  59. package/dist/utils/frameworkBin.js +48 -0
  60. package/dist/utils/indexedXmlLookup.d.ts +54 -0
  61. package/dist/utils/indexedXmlLookup.js +103 -0
  62. package/package.json +3 -1
package/README.md CHANGED
@@ -7,10 +7,10 @@
7
7
  [![npm](https://img.shields.io/npm/v/d365fo-mcp.svg?logo=npm&color=cb3837)](https://www.npmjs.com/package/d365fo-mcp)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
9
  [![Node.js](https://img.shields.io/badge/node-%3E%3D24.0.0-brightgreen.svg)](https://nodejs.org/)
10
- [![TypeScript](https://img.shields.io/badge/TypeScript-6.0-blue.svg)](https://www.typescriptlang.org/)
11
- [![Tests](https://img.shields.io/badge/tests-1400%2B-brightgreen.svg)](docs/TESTING.md)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-7.0-blue.svg)](https://www.typescriptlang.org/)
11
+ [![Tests](https://img.shields.io/badge/tests-2500%2B-brightgreen.svg)](docs/TESTING.md)
12
12
  <!-- coverage-badge:start -->
13
- [![Core coverage](https://img.shields.io/badge/core_coverage-100%25-brightgreen.svg)](eval/COVERAGE.md) [![Total coverage](https://img.shields.io/badge/total_coverage-85.9%25-lightgrey.svg)](eval/COVERAGE.md)
13
+ [![Core coverage](https://img.shields.io/badge/core_coverage-100%25-brightgreen.svg)](eval/COVERAGE.md) [![Total coverage](https://img.shields.io/badge/total_coverage-100%25-lightgrey.svg)](eval/COVERAGE.md)
14
14
  <!-- coverage-badge:end -->
15
15
 
16
16
  *Grounded AI development for Dynamics 365 Finance & Operations — works with GitHub Copilot and Claude Code*
@@ -51,7 +51,7 @@ This server pre-indexes your entire D365FO installation (580 000+ symbols across
51
51
  | 🔍 **Full-codebase intelligence** | 580K+ symbols indexed: classes, tables, forms, EDTs, enums, labels (20M+ rows), security artifacts — FTS5 search in < 10 ms |
52
52
  | 🛡️ **Grounded generation** | Fail-closed gates: `prepare` issues grounding tokens, `validate_code(mode="references")` proves every identifier, `validate_code(mode="syntax")` enforces best practices — hallucinated code never reaches disk |
53
53
  | 🧩 **Form pattern engine** | Complete catalog of Microsoft form patterns and sub-patterns: recommends the right pattern, clones reference forms with datasource re-binding, **deterministically expands** patterns that have no reference form, **auto-repairs** a form's missing required controls, validates structure and blocks invalid writes |
54
- | ✍️ **Safe metadata writes** | C# bridge uses Microsoft's own `IMetadataProvider` — no string-replacement XML corruption, automatic `.rnrproj` registration, one-call undo |
54
+ | ✍️ **Safe metadata writes** | C# bridge uses Microsoft's own `IMetadataProvider` wherever it can express the object; the few types and ops it cannot go through structured XML writers with ambiguity guards never blind string replacement. Automatic `.rnrproj` registration, one-call undo |
55
55
  | 🏗️ **SDLC integration** | MSBuild compilation with structured diagnostics, DB sync, xppbp best practices, SysTestRunner — all from chat |
56
56
  | 📐 **X++ knowledge base** | Queryable rules: select grammar, CoC authoring, financial dimensions, the posting engine (`LedgerVoucher`), number sequences, `SysExtension`, Electronic Reporting, AX2012→D365FO migration — prevents deprecated APIs |
57
57
 
@@ -120,6 +120,7 @@ Deployment guide: [docs/SETUP_AZURE.md](docs/SETUP_AZURE.md) — includes CI/CD
120
120
  | [Claude Code setup](docs/SETUP.md#claude-code-cli) | [Configuration](docs/CONFIGURATION.md) | [Testing](docs/TESTING.md) |
121
121
  | [Usage examples](docs/USAGE_EXAMPLES.md) — real tool chains | [Architecture](docs/ARCHITECTURE.md) | [Custom / ISV models](docs/CUSTOM_EXTENSIONS.md) |
122
122
  | | | [Coverage](eval/COVERAGE.md) — what the badge counts |
123
+ | | | [Eval loop](docs/AGENT_EVAL_LOOP.md) — the self-improvement harness |
123
124
 
124
125
  ## License
125
126
 
@@ -457,11 +457,18 @@ namespace D365MetadataBridge.Protocol
457
457
  ?? throw new ArgumentException("Missing: objectName");
458
458
  var fieldName = request.GetStringParam("fieldName")
459
459
  ?? throw new ArgumentException("Missing: fieldName");
460
+ // dataField/dataSource select the data-entity-extension mapped-field
461
+ // path inside AddField; both are absent for table/table-extension.
462
+ // This is the single-op RPC that BridgeClient.addField() calls —
463
+ // it must forward the same parameters as the batch-modify case below.
460
464
  return _writeService!.AddField(tableName, fieldName,
461
465
  request.GetStringParam("fieldType") ?? "String",
462
466
  request.GetStringParam("edt"),
463
467
  request.GetBoolParam("mandatory") ?? false,
464
- request.GetStringParam("label"));
468
+ request.GetStringParam("label"),
469
+ request.GetStringParam("dataField"),
470
+ request.GetStringParam("dataSource"),
471
+ request.GetStringParam("fieldGroupName"));
465
472
  });
466
473
 
467
474
  case "setproperty":
@@ -528,6 +535,53 @@ namespace D365MetadataBridge.Protocol
528
535
  return _writeService!.RemoveIndex(tableName, indexName);
529
536
  });
530
537
 
538
+ // This is the single-op RPC that BridgeClient.addFullTextIndex()/
539
+ // addTableMapping() actually call — it must carry the same four cases
540
+ // HandleBatchModify() has below, or every real request dies with
541
+ // "-32601 Unknown method" no matter how correct the C# write path is.
542
+ case "addfulltextindex":
543
+ return HandleWrite(request, () =>
544
+ {
545
+ var tableName = request.GetStringParam("objectName")
546
+ ?? throw new ArgumentException("Missing: objectName");
547
+ var indexName = request.GetStringParam("indexName")
548
+ ?? throw new ArgumentException("Missing: indexName");
549
+ return _writeService!.AddFullTextIndex(tableName, indexName,
550
+ request.GetParam<System.Collections.Generic.List<string>>("fields"));
551
+ });
552
+
553
+ case "removefulltextindex":
554
+ return HandleWrite(request, () =>
555
+ {
556
+ var tableName = request.GetStringParam("objectName")
557
+ ?? throw new ArgumentException("Missing: objectName");
558
+ var indexName = request.GetStringParam("indexName")
559
+ ?? throw new ArgumentException("Missing: indexName");
560
+ return _writeService!.RemoveFullTextIndex(tableName, indexName);
561
+ });
562
+
563
+ case "addtablemapping":
564
+ return HandleWrite(request, () =>
565
+ {
566
+ var tableName = request.GetStringParam("objectName")
567
+ ?? throw new ArgumentException("Missing: objectName");
568
+ var mapName = request.GetStringParam("mapName")
569
+ ?? throw new ArgumentException("Missing: mapName");
570
+ return _writeService!.AddTableMapping(tableName, mapName,
571
+ request.GetStringParam("mappingTable"),
572
+ request.GetParam<System.Collections.Generic.List<WriteMappingConnection>>("connections"));
573
+ });
574
+
575
+ case "removetablemapping":
576
+ return HandleWrite(request, () =>
577
+ {
578
+ var tableName = request.GetStringParam("objectName")
579
+ ?? throw new ArgumentException("Missing: objectName");
580
+ var mapName = request.GetStringParam("mapName")
581
+ ?? throw new ArgumentException("Missing: mapName");
582
+ return _writeService!.RemoveTableMapping(tableName, mapName);
583
+ });
584
+
531
585
  case "addrelation":
532
586
  return HandleWrite(request, () =>
533
587
  {
@@ -588,7 +642,12 @@ namespace D365MetadataBridge.Protocol
588
642
  ?? throw new ArgumentException("Missing: fieldGroupName");
589
643
  var fieldName = request.GetStringParam("fieldName")
590
644
  ?? throw new ArgumentException("Missing: fieldName");
591
- return _writeService!.AddFieldToFieldGroup(tableName, groupName, fieldName);
645
+ // This is the single-op RPC BridgeClient.addFieldToFieldGroup() actually
646
+ // calls — dropping extendBaseFieldGroup here silently reverts every
647
+ // caller to the extension-owns-the-group path (see HandleBatchModify()
648
+ // below, which forwards it correctly).
649
+ return _writeService!.AddFieldToFieldGroup(tableName, groupName, fieldName,
650
+ request.GetBoolParam("extendBaseFieldGroup") ?? false);
592
651
  });
593
652
 
594
653
  case "modifyfield":
@@ -911,12 +970,17 @@ namespace D365MetadataBridge.Protocol
911
970
 
912
971
  case "addfield":
913
972
  case "add-field":
973
+ // dataField/dataSource select the data-entity-extension mapped-field
974
+ // path inside AddField; both are absent for table/table-extension.
914
975
  writeResult = _writeService.AddField(objectName,
915
976
  S("fieldName") ?? throw new ArgumentException("Missing: fieldName"),
916
977
  S("fieldType") ?? "String",
917
978
  S("edt"),
918
979
  B("mandatory") ?? false,
919
- S("label"));
980
+ S("label"),
981
+ S("dataField"),
982
+ S("dataSource"),
983
+ S("fieldGroupName"));
920
984
  break;
921
985
 
922
986
  case "modifyfield":
@@ -963,6 +1027,33 @@ namespace D365MetadataBridge.Protocol
963
1027
  S("indexName") ?? throw new ArgumentException("Missing: indexName"));
964
1028
  break;
965
1029
 
1030
+ case "addfulltextindex":
1031
+ case "add-full-text-index":
1032
+ writeResult = _writeService.AddFullTextIndex(objectName,
1033
+ S("indexName") ?? throw new ArgumentException("Missing: indexName"),
1034
+ op.GetTypedParam<System.Collections.Generic.List<string>>("fields"));
1035
+ break;
1036
+
1037
+ case "removefulltextindex":
1038
+ case "remove-full-text-index":
1039
+ writeResult = _writeService.RemoveFullTextIndex(objectName,
1040
+ S("indexName") ?? throw new ArgumentException("Missing: indexName"));
1041
+ break;
1042
+
1043
+ case "addtablemapping":
1044
+ case "add-table-mapping":
1045
+ writeResult = _writeService.AddTableMapping(objectName,
1046
+ S("mapName") ?? throw new ArgumentException("Missing: mapName"),
1047
+ S("mappingTable"),
1048
+ op.GetTypedParam<System.Collections.Generic.List<WriteMappingConnection>>("connections"));
1049
+ break;
1050
+
1051
+ case "removetablemapping":
1052
+ case "remove-table-mapping":
1053
+ writeResult = _writeService.RemoveTableMapping(objectName,
1054
+ S("mapName") ?? throw new ArgumentException("Missing: mapName"));
1055
+ break;
1056
+
966
1057
  case "addrelation":
967
1058
  case "add-relation":
968
1059
  writeResult = _writeService.AddRelation(objectName,
@@ -998,7 +1089,8 @@ namespace D365MetadataBridge.Protocol
998
1089
  case "add-field-to-field-group":
999
1090
  writeResult = _writeService.AddFieldToFieldGroup(objectName,
1000
1091
  S("fieldGroupName") ?? S("groupName") ?? throw new ArgumentException("Missing: fieldGroupName"),
1001
- S("fieldName") ?? throw new ArgumentException("Missing: fieldName"));
1092
+ S("fieldName") ?? throw new ArgumentException("Missing: fieldName"),
1093
+ B("extendBaseFieldGroup") ?? false);
1002
1094
  break;
1003
1095
 
1004
1096
  case "addenumvalue":