Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ This CLI pre-indexes your entire D365FO installation (hundreds of thousands of s
| Labels | Hardcoded strings | Right `@SYS`/`@MODULE` key found instantly |
| Security chains | Hours of manual tracing | Role → Duty → Privilege → Entry Point in one call |
| Generated code | Hallucinated fields and types | Every reference proven against the index, gated before write |
| Agent context cost | 20–26 MCP tool schemas every turn | 1 shell tool + lazy-loaded Skills (~85 % fewer tokens) |
| Agent context cost | 24–26 MCP tool schemas every turn | 1 shell tool + lazy-loaded Skills (~85 % fewer tokens) |

---

Expand Down Expand Up @@ -220,7 +220,7 @@ Reference the `SKILL.md` files from `skills/anthropic/` in your session prompt o

### MCP (Claude Desktop, Continue, VS Code MCP)

The bundled `d365fo-mcp` adapter speaks JSON-RPC 2.0 over the same index. Its tool surface is **consolidated** into 22 discriminator-based tools (e.g. `search`, `get_object_info`, `get_method`, `labels`, `security_info`, `extension_info`, `object_patterns`, `generate_object`, `modify_method`, `analyze`, `models`) — see [docs/MIGRATION_FROM_MCP.md](docs/MIGRATION_FROM_MCP.md):
The bundled `d365fo-mcp` adapter speaks JSON-RPC 2.0 over the same index. Its tool surface is **consolidated** into 24 discriminator-based tools (e.g. `search`, `get_object_info`, `get_method`, `labels`, `security_info`, `extension_info`, `object_patterns`, `generate_object`, `modify_method`, `analyze`, `models`) — see [docs/MIGRATION_FROM_MCP.md](docs/MIGRATION_FROM_MCP.md):

```json
{
Expand Down Expand Up @@ -250,11 +250,11 @@ A `d365fo search` shell call returning results from your codebase = you're conne

## Why CLI instead of MCP?

MCP servers inject every tool definition into the model's context on every single turn. Tool consolidation trimmed that surface — the upstream MCP server went from ~61 per-type tools (≈3,500 tok/turn) to 26 discriminator-based tools, and this repo's adapter to 20 — but it is still ~1,800 tokens per turn versus one shell tool.
MCP servers inject every tool definition into the model's context on every single turn. Tool consolidation trimmed that surface — the upstream MCP server went from ~61 per-type tools (≈3,500 tok/turn) to 26 discriminator-based tools, and this repo's adapter to 24 — but it is still ~1,800 tokens per turn versus one shell tool.

| | MCP server | CLI + Skills |
|---|---|---|
| Tool definitions per turn | 20–26 tools (~1,800 tokens) | 1 shell tool (~100 tokens) |
| Tool definitions per turn | 24–26 tools (~1,800 tokens) | 1 shell tool (~100 tokens) |
| Discovery round-trips | 2–3 per task | often 1 (`d365fo prepare change`) |
| Scriptable (shell, CI/CD) | No | Yes |
| Works in any AI harness | No — MCP hosts only | Yes — Copilot, Claude, Codex, Gemini, … |
Expand All @@ -277,7 +277,7 @@ See [docs/TOKEN_ECONOMICS.md](docs/TOKEN_ECONOMICS.md) for the full analysis and
| **Form patterns** | `form-pattern analyze` (advisor), `form-pattern spec` (catalog), `form-pattern validate` (FP001–FP010) — mirrors the MCP `object_patterns` tool (`domain=form`) |
| **Find** | `find related`, `find coc`, `find relations`, `find usages`, `find extensions`, `find event-handlers`, `find references`, `find form-patterns`, `find batch-jobs` (the extensibility ones mirror the MCP `extension_info` tool) |
| **Read** | `read class`, `read table`, `read form` (= MCP `get_method`) |
| **Generate** | `generate table\|class\|coc\|form\|entity\|extension\|event-handler\|privilege\|duty\|role\|report\|sysoperation\|number-sequence\|workflow\|menu-item\|edt\|enum\|query\|business-event\|custom-service\|migration-script\|runbase\|security-policy\|systest` |
| **Generate** | `generate table\|class\|coc\|form\|datasource-method\|control-method\|simple-list\|entity\|extension\|event-handler\|privilege\|duty\|role\|report\|sysoperation\|number-sequence\|workflow\|menu-item\|edt\|enum\|query\|view\|map\|business-event\|custom-service\|migration-script\|runbase\|security-policy\|systest` |
| **Labels** | `labels search\|resolve\|info\|create\|rename\|delete` — search/resolve plus in-place `*.label.txt` edits, multi-language via `--lang` (mirrors the MCP `labels` tool) |
| **Journal / undo** | `undo [--steps N] [--dry-run]`, `journal list`, `delete` (kind/name, bridge or on-disk) — deterministic single-command rollback for every write path (mirrors the MCP `undo_last_modification` tool) |
| **Analyze** | `analyze completeness`, `analyze integration`, `analyze impact`, `lint`, `suggest edt`, `suggest extension`, `report-integrations` |
Expand Down
22 changes: 17 additions & 5 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@ Commands:
| `insert-in-loop` | `.insert()` call inside a loop body — suggest `RecordInsertList` (`BPCheckInsertMethodInLoop`) | warning |
| `tts-try-catch` | `try` block inside `ttsbegin`/`ttscommit` without catching `UpdateConflict` (`BPCheckNoTTSTryBlock`) | warning |
| `empty-table-method` | Table method override with empty body — forces row-by-row DB ops (`BPCheckEmptyTableMethod`) | warning |
| `runbase-no-can-go-batch` | `RunBaseBatch` subclass without `canGoBatch() { return true; }` (`BPCheckBatchJobsEnabled`) | warning |
| `batch-no-cango` | `RunBaseBatch` subclass without `canGoBatch() { return true; }` (`BPCheckBatchJobsEnabled`) | warning |
| `force-literals` | `forceLiterals` in a select — SQL injection risk | error |
| `public-instance-field` | Public instance fields on a class — violates encapsulation | warning |
| `cache-lookup-mismatch` | `CacheLookup` value inconsistent with `TableGroup` (`BPCheckTablePropertyMismatch`) | warning |
| `missing-delete-action` | Table relations without `DeleteAction` or `OnDelete` configured (`BPCheckMissingDeleteActions`) | warning |
| `no-alternate-key` | Tables with unique indexes but no `AlternateKey = Yes` index (`BPCheckAlternateKeyAbsent`) | warning |
| `unknown-label-ref` | Label `@File:Key` references in source that don't resolve in the `Labels` table (`BPErrorUnknownLabel`) | error |

Use `--category <name>[,<name>…]` to run specific rules. `--format sarif` emits SARIF 2.1.0 for CI.

Expand All @@ -137,10 +137,10 @@ Use `--category <name>[,<name>…]` to run specific rules. `--format sarif` emit
`D365FO.Core.FormPatterns` ports the MCP server's form pattern engine: a
data-driven catalog of Microsoft form patterns plus a pure structural validator.

**Catalog** (`FormPatternCatalog`): 18 top-level patterns (SimpleList,
**Catalog** (`FormPatternCatalog`): 20 top-level patterns (SimpleList,
SimpleListDetails, DetailsMaster ±Tabs, DetailsTransaction, Dialog, DropDialog,
TableOfContents, Lookup, ListPage, Workspace ±Operational, Form Part / FactBox
variants, Simple Details, legacy Task patterns, Wizard) and 19 container
variants, Simple Details, legacy Task patterns, Wizard) and 16 container
sub-patterns (FieldsFieldGroups, CustomAndQuickFilters, SidePanel,
ToolbarAndList, workspace sections, …). Each spec encodes what the Visual
Studio pattern engine enforces — required containers, ordering, allowed child
Expand Down Expand Up @@ -194,12 +194,24 @@ Provides: authoritative per-object reads (`get` commands), file create/update/de

## MCP coexistence

`D365FO.Mcp` forwards to the same `D365FO.Core` primitives as the CLI. It speaks the `ModelContextProtocol` C# SDK over stdio and exposes **20 consolidated, discriminator-based tools** (a single tool dispatches on a `type` / `objectType` / `mode` / `action` / `domain` / `include` field — mirroring the upstream `d365fo-mcp-server`). Index, bridge, and guardrails are shared — both adapters see identical data.
`D365FO.Mcp` forwards to the same `D365FO.Core` primitives as the CLI. It speaks the `ModelContextProtocol` C# SDK over stdio and exposes **24 consolidated, discriminator-based tools** (a single tool dispatches on a `type` / `objectType` / `mode` / `action` / `domain` / `include` field — mirroring the upstream `d365fo-mcp-server`). Index, bridge, and guardrails are shared — both adapters see identical data.

Adding or consolidating a tool: edit `ToolCatalog` (the discriminator binder) + the backing methods on `ToolHandlers`. The CLI picks it up once a command wraps the same `MetadataRepository` call; keep each command's `mcpTool` label in `SchemaCommand` pointing at the unified tool.

**Daemon mode** (`d365fo daemon start`) keeps the SQLite handle and read caches hot. Also starts a `FileSystemWatcher` that auto-triggers incremental `index refresh` when `*.xml` files change (debounce 3 s; disable with `--no-watch`).

## HTTP transport

`D365FO.Mcp` can run `--http --port <p>` instead of stdio (`POST /mcp`, `GET /health`). Auth is an `X-Api-Key` header, checked against the `API_KEY` environment variable; if unset the endpoint runs unauthenticated and logs a startup warning. `MCP_SERVER_MODE` (`full` / `read-only` / `write-only`) gates the tool surface on both transports. See [docs/CAPABILITIES.md](CAPABILITIES.md) for the full env-var table.

## Modification journal & undo

Every metadata write appends an entry to a FIFO-pruned journal at `<index-dir>/journal/`. `d365fo undo [--steps N] [--dry-run]` replays entries in reverse through the same write path that produced them. `d365fo modify method` (structured method-body replace via the Bridge) belongs to this same write-tracking system — its writes are journaled and undoable like any other.

## Editor connect

`d365fo connect <url>` points a local MCP client config (`.mcp.json` for Claude, `.vscode/mcp.json` for VS Code) at a deployed HTTP `D365FO.Mcp` instance — writes the server entry, optional `X-Api-Key`, and probes `GET /health` before saving.

---

## See also
Expand Down
6 changes: 3 additions & 3 deletions docs/CAPABILITIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,12 @@ d365fo lint --format sarif > lint.sarif # SARIF 2.1.0 for CI
| `insert-in-loop` | `.insert()` inside a loop body — suggest `RecordInsertList` | warning |
| `tts-try-catch` | `try` inside `ttsbegin`/`ttscommit` without catching `UpdateConflict` | warning |
| `empty-table-method` | Table method override with empty body | warning |
| `runbase-no-can-go-batch` | `RunBaseBatch` subclass without `canGoBatch() { return true; }` | warning |
| `batch-no-cango` | `RunBaseBatch` subclass without `canGoBatch() { return true; }` | warning |
| `force-literals` | `forceLiterals` in a select — SQL injection risk | error |
| `public-instance-field` | Public instance fields on a class — violates encapsulation | warning |
| `cache-lookup-mismatch` | `CacheLookup` inconsistent with `TableGroup` | warning |
| `missing-delete-action` | Table relation without `DeleteAction` or `OnDelete` | warning |
| `no-alternate-key` | Tables with unique indexes but no `AlternateKey` | warning |
| `unknown-label-ref` | `@File:Key` label references that don't resolve in the index | error |

---

Expand Down Expand Up @@ -343,7 +343,7 @@ Returns `UNSUPPORTED_PLATFORM` on non-Windows.

## MCP server

Exposes the same index and scaffolding surface as the CLI over the `ModelContextProtocol` C# SDK via stdio (default) or HTTP (`--http`, for a shared team deployment). The tool surface is **consolidated** into **22 discriminator-based tools** (`search`, `get_object_info`, `get_method`, `labels`, `security_info`, `extension_info`, `object_patterns`, `generate_object`, `modify_method`, `analyze`, `models`, …) instead of one tool per object type — mirroring the upstream `d365fo-mcp-server` (which sits at 26). A single tool dispatches on a `type` / `objectType` / `mode` / `action` / `domain` / `include` field. See [MIGRATION_FROM_MCP.md](MIGRATION_FROM_MCP.md) for the full old→new mapping.
Exposes the same index and scaffolding surface as the CLI over the `ModelContextProtocol` C# SDK via stdio (default) or HTTP (`--http`, for a shared team deployment). The tool surface is **consolidated** into **24 discriminator-based tools** (`search`, `get_object_info`, `get_method`, `labels`, `security_info`, `extension_info`, `object_patterns`, `generate_object`, `modify_method`, `analyze`, `models`, …) instead of one tool per object type — mirroring the upstream `d365fo-mcp-server` (which sits at 26). A single tool dispatches on a `type` / `objectType` / `mode` / `action` / `domain` / `include` field. See [MIGRATION_FROM_MCP.md](MIGRATION_FROM_MCP.md) for the full old→new mapping.

```jsonc
{
Expand Down
4 changes: 3 additions & 1 deletion docs/followups/upstream-port-2026-08.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ Resolved so far:
npm-full-install), `722d0e7` "trusted publishing and script guard",
`88270f7` `refactor/node-sqlite-drop-native-dep`, `753`/`754`
(`better-sqlite3` → `node:sqlite`, SQLite startup preflight). This CLI
ships as a .NET global tool via NuGet; none of upstream's npm-registry /
currently ships via `install.ps1` (git clone + `dotnet publish`
self-contained binary) rather than a package registry — see
`install.ps1`'s header comment; none of upstream's npm-registry /
Node-native-module distribution concerns apply.
- **Setup wizard / Copilot & MCP-client bootstrap**: `281d7db`
`feat(setup): configure the server through a wizard, not a hand-edited
Expand Down
10 changes: 5 additions & 5 deletions docs/img/solution-architecture-diagram.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading