grp-mcp 0.2.0__tar.gz

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 (32) hide show
  1. grp_mcp-0.2.0/.env.example +30 -0
  2. grp_mcp-0.2.0/.gitignore +25 -0
  3. grp_mcp-0.2.0/How-to-start-webapp-change-instance.txt +2 -0
  4. grp_mcp-0.2.0/PKG-INFO +381 -0
  5. grp_mcp-0.2.0/README.md +367 -0
  6. grp_mcp-0.2.0/connections.example.json +34 -0
  7. grp_mcp-0.2.0/playwright/EXTENDING_ENDPOINTS.md +192 -0
  8. grp_mcp-0.2.0/playwright/add_endpoint_action.js +121 -0
  9. grp_mcp-0.2.0/playwright/add_endpoint_action_modern.js +124 -0
  10. grp_mcp-0.2.0/playwright/add_endpoint_entity.js +167 -0
  11. grp_mcp-0.2.0/playwright/add_endpoint_entity_modern.js +146 -0
  12. grp_mcp-0.2.0/playwright/change_company_type_modern.js +54 -0
  13. grp_mcp-0.2.0/playwright/delete_endpoint_entity_modern.js +46 -0
  14. grp_mcp-0.2.0/playwright/delete_finyear.js +39 -0
  15. grp_mcp-0.2.0/playwright/dump_full_schema.py +113 -0
  16. grp_mcp-0.2.0/playwright/inspect_company_fields.js +53 -0
  17. grp_mcp-0.2.0/playwright/inspect_grid_fields.js +50 -0
  18. grp_mcp-0.2.0/playwright/inspect_screen_fields.js +43 -0
  19. grp_mcp-0.2.0/playwright/inspect_segkey.js +41 -0
  20. grp_mcp-0.2.0/playwright/inspect_sm207060.js +36 -0
  21. grp_mcp-0.2.0/playwright/inspect_tabs_cs101500.js +72 -0
  22. grp_mcp-0.2.0/playwright/populate_all_views.js +104 -0
  23. grp_mcp-0.2.0/playwright/set_features_modern.js +77 -0
  24. grp_mcp-0.2.0/playwright/setup_finyear.js +120 -0
  25. grp_mcp-0.2.0/pyproject.toml +26 -0
  26. grp_mcp-0.2.0/src/grp_mcp/__init__.py +3 -0
  27. grp_mcp-0.2.0/src/grp_mcp/acumatica.py +505 -0
  28. grp_mcp-0.2.0/src/grp_mcp/config.py +157 -0
  29. grp_mcp-0.2.0/src/grp_mcp/customization.py +167 -0
  30. grp_mcp-0.2.0/src/grp_mcp/loaders.py +75 -0
  31. grp_mcp-0.2.0/src/grp_mcp/server.py +1436 -0
  32. grp_mcp-0.2.0/src/grp_mcp/ui.py +319 -0
@@ -0,0 +1,30 @@
1
+ # Default Acumatica instance (used when a tool is called without an `instance` arg).
2
+ # For multiple instances, use connections.json instead (see connections.example.json).
3
+
4
+ GRP_MCP_BASE_URL=https://your-instance.acumatica.com
5
+ GRP_MCP_CLIENT_ID=YOUR_CLIENT_ID@CompanyLogin
6
+ GRP_MCP_CLIENT_SECRET=your_client_secret
7
+ GRP_MCP_USERNAME=admin
8
+ GRP_MCP_PASSWORD=your_password
9
+
10
+ # Optional. Defaults shown.
11
+ GRP_MCP_ENDPOINT_NAME=Default
12
+ GRP_MCP_ENDPOINT_VERSION=24.200.001
13
+ # Tenant/company login name, used for OData (Generic Inquiry) calls and
14
+ # Customization API cookie login.
15
+ GRP_MCP_TENANT=
16
+ # Optional login branch.
17
+ GRP_MCP_BRANCH=
18
+ # --- write gates (default read-only; opt in) ---
19
+ # Allow record mutations (create/update, load, actions, import-scenario, note, attach).
20
+ GRP_MCP_ALLOW_WRITE=false
21
+ # Allow record deletes (stricter than write).
22
+ GRP_MCP_ALLOW_DELETE=false
23
+ # Gate Customization API writes (publish/import/unpublish). Default off.
24
+ # WARNING: publishing is website-level and affects ALL tenants on the instance.
25
+ GRP_MCP_ALLOW_PUBLISH=false
26
+ # Filesystem sandbox (read_roots / write_roots / max_file_bytes) is configured via
27
+ # connections.json only — see connections.example.json. Empty roots = unrestricted.
28
+
29
+ # Path to a JSON file holding multiple named instances (optional).
30
+ # GRP_MCP_CONNECTIONS=C:\Temp\grp-mcp\connections.json
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+ env/
11
+
12
+ # Secrets / local config
13
+ .env
14
+ connections.json
15
+
16
+ # IDE
17
+ .vscode/
18
+ .idea/
19
+ .DS_Store
20
+
21
+ # Snapshot dumps (business data)
22
+ snapshots/
23
+
24
+ # Playwright debug screenshots
25
+ playwright/shots/
@@ -0,0 +1,2 @@
1
+ PS C:\Users\CSM-Arvindh\OneDrive - Censof Holdings\Desktop\OPEX\grp-mcp> grp-mcp-ui
2
+ grp-mcp config UI -> http://127.0.0.1:8765 (Ctrl+C to stop)
grp_mcp-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,381 @@
1
+ Metadata-Version: 2.4
2
+ Name: grp-mcp
3
+ Version: 0.2.0
4
+ Summary: MCP server exposing Acumatica ERP (contract-based REST API) as tools for AI agents. Multi-instance, OAuth2.
5
+ Author: Arvindh
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: httpx>=0.27
9
+ Requires-Dist: mcp>=1.2.0
10
+ Requires-Dist: openpyxl>=3.1
11
+ Requires-Dist: pydantic>=2.6
12
+ Requires-Dist: python-dotenv>=1.0
13
+ Description-Content-Type: text/markdown
14
+
15
+ # grp-mcp
16
+
17
+ MCP server that exposes **Acumatica ERP** (contract-based REST API) as tools for
18
+ AI agents. Multi-instance, OAuth2. Point it at any Acumatica site by giving it a
19
+ base URL + OAuth credentials.
20
+
21
+ ## Tools
22
+
23
+ **Discovery / metadata**
24
+
25
+ | Tool | What it does |
26
+ |------|--------------|
27
+ | `list_instances` | List configured profiles + which is active (no secrets). |
28
+ | `add_instance` | Add/replace a connection profile and save it to connections.json. |
29
+ | `set_active_instance` | Choose the default profile (session, or persisted). |
30
+ | `remove_instance` | Remove a profile (and drop its cached session). |
31
+ | `test_connection` | Verify a profile's credentials (token + contract read). |
32
+ | `list_endpoints` | List all web service endpoints on the instance (name/version). |
33
+ | `list_entities` | List top-level entities of the configured endpoint (via swagger.json). |
34
+ | `get_entity_schema` | Fields of one entity, split into scalar vs detail (nested). `deep=true` returns the full tree with every detail tab expanded to its nested fields. |
35
+ | `list_actions` | Actions invokable on an entity (for `invoke_action`). |
36
+ | `list_generic_inquiries` | Generic Inquiries exposed via OData (name + url). |
37
+ | `list_dacs` | List every DAC exposed via the DAC-based OData v4 interface. |
38
+ | `get_dac_metadata` | Read a DAC's field definitions from the OData CSDL ($metadata) incl. mandatory flags (`Nullable=false`/key). Covers single-row config DACs `run_dac_odata` can't. |
39
+
40
+ **Read**
41
+
42
+ | Tool | What it does |
43
+ |------|--------------|
44
+ | `get_entity` | Get one record or a filtered list; supports `$filter/$select/$expand/$top/$skip/$custom`. |
45
+ | `fetch_all_entities` | Retrieve **all** records of an entity, auto-paging with `$top/$skip`. |
46
+ | `count_entity` | Count records (client-side, auto-paged; scope with `filter`). |
47
+ | `run_generic_inquiry` | Run a Generic Inquiry via OData. |
48
+ | `run_dac_odata` | Query a single DAC via OData v4 (reaches tables **not** on the endpoint). |
49
+ | `list_attachments` | List files attached to a record (name + download href). |
50
+ | `download_file` | Download a record's attached file to disk. |
51
+ | `get_endpoint_definition` | Read an endpoint's contract (entity tree/props) from SM207060. |
52
+
53
+ **Write**
54
+
55
+ | Tool | What it does |
56
+ |------|--------------|
57
+ | `create_or_update_entity` | Create/update a record (PUT, upsert by key). |
58
+ | `load_from_excel` | Bulk upsert an entity from `.xlsx`/`.csv` with column mapping + dry-run. |
59
+ | `setup_data_provider` | Create + fully configure a Data Provider (SM206015) from a data file (schema written directly from its header; optional file upload). |
60
+ | `attach_file` | Upload a file and attach it to a record (`files:put`). |
61
+ | `set_note` | Set/clear a record's Note text. |
62
+ | `delete_entity` | Delete a record by id. |
63
+ | `invoke_action` | Run a record action (Release, ConfirmShipment, …). |
64
+ | `run_import_scenario` | Drive Import-by-Scenario (SM206036): prepare (+ optional import). |
65
+ | `run_report` | Run a Report-type entity and save the rendered file (PDF) to disk. |
66
+ | `poll_action` | Check a long-running action's status by its `Location`. |
67
+
68
+ **Contract / config**
69
+
70
+ | Tool | What it does |
71
+ |------|--------------|
72
+ | `extend_endpoint` | **Verified no-op over REST** — kept for reference; extend endpoints via the SM207060 UI / playwright or a customization project instead. |
73
+
74
+ **Safety**
75
+
76
+ | Tool | What it does |
77
+ |------|--------------|
78
+ | `snapshot_entity` | Dump an entity to JSON before risky changes (rollback aid). |
79
+
80
+ **Customization Web API**
81
+
82
+ | Tool | What it does |
83
+ |------|--------------|
84
+ | `list_published` | List published customization projects (read-only). |
85
+ | `export_customization` | Export a project to a `.zip` on disk (headless edit loop). |
86
+ | `import_customization` | Import a customization `.zip` (does not publish). |
87
+ | `publish_customization` | Publish projects (async begin + poll). |
88
+ | `unpublish_customization` | Unpublish all customization projects (rollback). |
89
+
90
+ Every tool takes an optional `instance` arg to pick a connection; defaults to the
91
+ configured default instance.
92
+
93
+ The data tools use the **contract REST API** over OAuth2. The customization tools
94
+ use the **Customization Web API** over a cookie session (it rejects OAuth bearer);
95
+ both reuse the same credentials from your config.
96
+
97
+ ### Managing profiles at runtime
98
+
99
+ Every tool takes an optional `instance` arg to pick a profile; without it, the
100
+ **active** profile is used. You can manage profiles without hand-editing the file:
101
+
102
+ - `list_instances` — see all profiles, their endpoint/tenant/gates, and which is active.
103
+ - `add_instance(name, base_url, client_id, client_secret, username, password, …)` —
104
+ register a new profile (e.g. a second Acumatica site) and save it to
105
+ connections.json. Gates default to read-only; pass `set_active=true` to switch to it.
106
+ - `set_active_instance(name)` — change the default profile for subsequent calls
107
+ (`persist=true` also writes it as `default` in the file so it survives a restart).
108
+ - `remove_instance(name)` — drop a profile and its cached session.
109
+ - `test_connection(instance)` — confirm a profile's OAuth creds actually work.
110
+
111
+ Each profile needs its **own** Connected Application registered on **that** instance
112
+ (Integration → Connected Applications, Resource-Owner-Password flow). Because the
113
+ SSRF guard pins the OAuth token to each profile's own origin, you cannot reach a host
114
+ that isn't a configured profile — add it first. connections.json is gitignored, so
115
+ saved secrets never leave the machine.
116
+
117
+ ### Config UI (localhost)
118
+
119
+ Prefer a page over JSON/tools? Run the bundled config UI:
120
+
121
+ ```bash
122
+ grp-mcp-ui # or: python -m grp_mcp.ui
123
+ # -> http://127.0.0.1:8765
124
+ ```
125
+
126
+ A single-file, dependency-free (stdlib `http.server`) page to **list / add / edit /
127
+ set-active / remove / test** profiles, writing the same connections.json. **First run
128
+ needs no config file** — on a fresh machine the page opens with an empty list; add your
129
+ first profile in the browser and it creates connections.json for you (no JSON editing).
130
+ It binds to `127.0.0.1` only (it edits credentials) and **never sends secrets to the
131
+ browser** —
132
+ the profile list only reports whether a secret/password is set. Leave the secret and
133
+ password blank when editing to keep the existing values. Because the MCP server reads
134
+ config at startup. To apply add/active changes to the live connector **without a
135
+ restart**, run the `reload_config` tool in Claude (it re-reads connections.json and
136
+ frees old sessions). Restarting the MCP also works. (Test works immediately — it opens
137
+ its own session.)
138
+
139
+ The header shows a build marker (e.g. `build 2`); if you don't see it after editing,
140
+ you're on a cached page or an old server process. Responses send `Cache-Control:
141
+ no-store`, so a hard refresh (Ctrl+Shift+R) is enough. If the page is blank or the
142
+ port won't bind, a previous instance is still holding it — find and stop it:
143
+
144
+ ```bash
145
+ # Windows: netstat -ano | findstr :8765 then taskkill /F /PID <pid>
146
+ # macOS/Linux: lsof -ti:8765 | xargs kill
147
+ ```
148
+
149
+ ### Bulk loading from Excel/CSV
150
+
151
+ `load_from_excel` turns a master file (Chart of Accounts, sub-account values,
152
+ trial balance, …) into one call instead of hundreds of `create_or_update_entity`.
153
+ The first row is the header; `column_map` maps a header to an entity field name
154
+ (omit to use headers verbatim, or map to `""` to ignore a column). It defaults to
155
+ `dry_run=true` — it parses, maps, and validates field names against the schema and
156
+ returns a preview **without writing**; re-run with `dry_run=false` to load. Only
157
+ scalar fields are supported (no nested detail rows).
158
+
159
+ ### Extending an endpoint contract
160
+
161
+ `extend_endpoint` is a **verified no-op over REST** and is kept only for reference.
162
+ `WebServiceEndpoints` (SM207060) is a stateful wizard form — its create/extend views
163
+ are transient and a PUT does nothing. To actually add entities/fields/actions to an
164
+ endpoint, use the **SM207060 UI** (drive it with the playwright scripts in
165
+ `playwright/`) or a **customization project** (`export_customization` → edit
166
+ `project.xml` → `import_customization` → `publish_customization`). Reading a contract
167
+ works fine via `get_endpoint_definition`.
168
+
169
+ ### Security model
170
+
171
+ This server holds ERP credentials and runs with the host user's privileges, so the
172
+ tools are sandboxed:
173
+
174
+ - **Token never leaves the instance.** Every authenticated request is checked
175
+ against the configured origin (`scheme://host`); a `poll_action`/download URL on
176
+ any other host is refused (prevents OAuth-token exfiltration / SSRF).
177
+ - **Writes are opt-in.** Record mutations (`create_or_update_entity`,
178
+ `load_from_excel`, `invoke_action`, `run_import_scenario`, `set_note`,
179
+ `attach_file`) require `"allow_write": true`; `delete_entity` requires the stricter
180
+ `"allow_delete": true`; customization publish/import/unpublish require
181
+ `"allow_publish": true`. **Default is read-only.**
182
+ - **Filesystem is fenced.** Tools that read (`attach_file`, `import_customization`,
183
+ `load_from_excel`) or write (`download_file`, `run_report`, `snapshot_entity`,
184
+ `export_customization`) a local path enforce `read_roots` / `write_roots` (a path
185
+ must sit inside an allowed dir if the list is set) and a `max_file_bytes` size cap
186
+ on reads. Leave the root lists empty only on a trusted single-user host.
187
+ - **Bounded loops.** Pagination and polling arguments are range-checked, so a
188
+ `page_size`/`poll_interval` of 0 can't spin forever.
189
+ - **Sessions released.** Token refreshes are serialized (one login, not N), and API
190
+ sessions are logged out on shutdown to free license seats.
191
+
192
+ ### Paging large tables
193
+
194
+ The contract API caps a single list GET, so a plain `get_entity` (no `record_id`)
195
+ can silently return **only the first page** of a big table. Two fixes:
196
+
197
+ - `get_entity` accepts `$skip` (the `skip` arg) to grab the next page manually.
198
+ - `fetch_all_entities` loops `$top`/`$skip` until the last (short) page and returns
199
+ `{count, records}` — use it whenever you need the **whole** table (full Chart of
200
+ Accounts, all vendors, …). `page_size` sets rows per request; `max_records` caps
201
+ early. `count_entity` and `snapshot_entity` auto-page too, so counts and snapshots
202
+ cover the full table rather than page 1.
203
+
204
+ ### DAC-based OData (data not on the endpoint)
205
+
206
+ The contract API only sees entities that were added to the endpoint in SM207060.
207
+ `list_dacs` + `run_dac_odata` reach data **directly from DACs** through the
208
+ DAC-based OData v4 interface (`<base>/t/<Tenant>/api/odata/dac/<DAC>`), bypassing
209
+ the endpoint entirely — handy for reading a screen/table you haven't exposed.
210
+ Read-only, and it needs the `tenant` (company login) set in config. `run_dac_odata`
211
+ supports `$filter/$select/$expand/$top/$skip`. Note `list_dacs` can return thousands
212
+ of DACs; it's best browsed with a known DAC name in hand.
213
+
214
+ **Mandatory-field discovery — `get_dac_metadata`.** `run_dac_odata` only reads DACs
215
+ exposed as OData *collections*; single-row config DACs (e.g. `GLSetup` = GL
216
+ Preferences, `FinYearSetup` = Financial Year) serve no collection route and 404.
217
+ `get_dac_metadata` reads the DAC OData CSDL (`<dac base>/$metadata`) instead, which
218
+ describes **every** DAC's fields — name, type, key, and `Nullable` flag. A field with
219
+ `Nullable="false"` (or a key field) is **mandatory** at the DB level. Args: `dac`
220
+ (filter to one entity type, case-insensitive; omit for all), `mandatory_only` (return
221
+ only required fields), `raw` (return the CSDL XML verbatim). The parser matches CSDL
222
+ tags by local name, so it's namespace/OData-version-proof.
223
+
224
+ Two gotchas it works around: this platform's OData layer **500s on JSON metadata**
225
+ ("only supported at platform implementing .NETStandard 2.0") and ignores `$format`, so
226
+ the tool requests `Accept: application/xml`. And `Nullable=false` is the **DB-enforced**
227
+ required set — graph-validated business-required fields (e.g. GL Preferences' Retained
228
+ Earnings / YTD Net Income accounts) are `Nullable=true` here and won't show; cross-check
229
+ the screen's KB form reference for those.
230
+
231
+ ### Attachments and reports
232
+
233
+ - `attach_file` uploads a file onto a record (`files:put`); `list_attachments`
234
+ lists what's attached (name + href); `download_file` pulls an attachment to disk.
235
+ - `run_report` runs a **Report-type** endpoint entity: it PUTs the report with its
236
+ parameters, polls the returned `Location` until the render completes, and writes
237
+ the file (usually PDF) to disk. The report must first be added to the endpoint as
238
+ a Report entity (see it in `list_entities`).
239
+
240
+ ### Detail-field guard
241
+
242
+ A list GET (no `record_id`) cannot return detail/nested collections — Acumatica
243
+ silently omits them. `get_entity` detects when a list query asks for a detail field
244
+ via `expand`/`select` and returns a `_warning` explaining the field is absent and
245
+ how to fetch it (per record, by key). `get_entity_schema` labels which fields are
246
+ detail so you know up front.
247
+
248
+ ### Publishing customization projects
249
+
250
+ Publishing is **website-level — it recompiles the site and affects ALL tenants**
251
+ on the instance, not just one. As a safety gate, `publish_customization`,
252
+ `import_customization`, and `unpublish_customization` are refused unless the
253
+ instance profile sets `"allow_publish": true`. Keep it `false` on prod profiles.
254
+
255
+ `publish_customization` runs the async flow automatically (`publishBegin` → poll
256
+ `publishEnd` until `isCompleted`). `tenant_mode` is `Current` (default), `All`, or
257
+ `List` (with `tenant_login_names`).
258
+
259
+ ## Setup
260
+
261
+ ### 1. Acumatica: register a Connected Application (OAuth2)
262
+
263
+ In Acumatica: **Integration → Connected Applications**. Create one with the
264
+ **Resource Owner Password Credentials** flow enabled. Note the **Client ID**
265
+ (looks like `GUID@CompanyLogin`) and **Client Secret**.
266
+
267
+ ### 2. Install
268
+
269
+ ```bash
270
+ git clone https://github.com/Arvindh95Censof/grp-mcp.git
271
+ cd grp-mcp
272
+ python -m venv .venv && .venv\Scripts\activate # Windows
273
+ pip install -e .
274
+ ```
275
+
276
+ Dependencies install automatically (`mcp`, `httpx`, `pydantic`, `python-dotenv`,
277
+ `openpyxl`). Note: the `.venv` is **not relocatable** — if you move the repo,
278
+ recreate the venv and `pip install -e .` at the new path, then update the launcher.
279
+
280
+ ### 3. Configure (pick one)
281
+
282
+ Credentials are read **once at server startup**, in this priority order:
283
+
284
+ 1. `GRP_MCP_CONNECTIONS` env var → path to a `connections.json`
285
+ 2. `connections.json` in the current working directory
286
+ 3. `connections.json` in the repo root
287
+ 4. `.env` file in the current working directory
288
+
289
+ **Option A — `.env` (simplest, single instance):** copy `.env.example` to `.env`
290
+ and fill it in. Only loaded if the server's launch directory is the repo, so it
291
+ works best with a launcher that sets `cwd` (see below).
292
+
293
+ **Option B — `connections.json` (robust, multi-instance):** copy
294
+ `connections.example.json` to `connections.json`, add one or more named profiles.
295
+ Recommended for distribution because you can point at it with an absolute path
296
+ that does not depend on the launch directory.
297
+
298
+ Both `.env` and `connections.json` are gitignored — never commit real credentials.
299
+
300
+ ### 4. Register with Claude
301
+
302
+ **Claude Code (CLI)** — user scope, available in all projects. Point at an
303
+ absolute `connections.json` so launch directory does not matter:
304
+
305
+ ```bash
306
+ claude mcp add grp-mcp -s user \
307
+ -e GRP_MCP_CONNECTIONS=/abs/path/to/connections.json \
308
+ -- /abs/path/to/.venv/Scripts/grp-mcp.exe # use grp-mcp on macOS/Linux
309
+ ```
310
+
311
+ **Claude Desktop** — add to `claude_desktop_config.json`:
312
+
313
+ ```json
314
+ {
315
+ "mcpServers": {
316
+ "grp-mcp": {
317
+ "command": "grp-mcp",
318
+ "cwd": "C:\\path\\to\\grp-mcp",
319
+ "env": { "GRP_MCP_CONNECTIONS": "C:\\path\\to\\grp-mcp\\connections.json" }
320
+ }
321
+ }
322
+ }
323
+ ```
324
+
325
+ (`cwd` lets `.env` load; the `env` line makes `connections.json` work regardless.
326
+ Use one config method — you don't need both files.)
327
+
328
+ Restart the client after adding — tools load at startup.
329
+
330
+ ## Notes
331
+
332
+ - Auth: OAuth2 resource-owner-password grant. Tokens auto-refresh.
333
+ - `endpoint_version` defaults to `24.200.001`; set it to match your instance's
334
+ Default endpoint version (System → Web Service Endpoints).
335
+ - Generic Inquiries are read via OData and need the `tenant` (company login) set.
336
+ - Actions may return `202` + a `Location` for long-running work — check it with
337
+ `poll_action` (204 = finished, 202 = still running).
338
+ - `snapshot_entity` writes to `<connections dir>/snapshots/` by default; that
339
+ folder is gitignored (it can contain business data).
340
+
341
+ ## Status
342
+
343
+ v0.2 — 38 tools (incl. runtime profile management). Covers the contract REST API (CRUD, actions, `$skip` paging,
344
+ attachments up/down, notes, reports), DAC + GI OData (incl. CSDL metadata / mandatory-field
345
+ discovery), import scenarios, and the
346
+ Customization Web API. By-design gap: endpoint **writes** (SM207060) are a stateful
347
+ wizard — do those via the SM207060 UI / playwright or a customization project, not
348
+ REST. Roadmap: nested detail rows in `load_from_excel`.
349
+
350
+ ## AFS Financial Report entities (instance-specific)
351
+
352
+ These custom entities are exposed on the **`Default2025` / `25.200.001`** endpoint
353
+ of the AFS Financial Report customization (`AFSCPFinancialReportv213032026`). They
354
+ are not part of grp-mcp itself — they were added in SM207060 and are reachable
355
+ through the generic tools. Documented here as a usage reference.
356
+
357
+ | Entity | Key field | Detail collection (use as `expand`) |
358
+ |--------|-----------|-------------------------------------|
359
+ | `FLRTReportDefinition` | `DefinitionCode` | `LineItems` → report line items |
360
+ | `FLRTGIDataSource` | `DataSourceCode` | `GIDataSource` → GI column defs |
361
+ | `FLRTFinancialReport` | `ReportCD` | `DefinitionLinks` → linked definitions |
362
+ | `FLRTPresentationGeneration` | `PresentationCD` | `PresentationDataSourceLink` → linked data sources |
363
+ | `FLRTTenantCredentials` | `CompanyNumber` | — |
364
+
365
+ Notes:
366
+ - Detail/expand names are **as configured in the endpoint**, which differ from the
367
+ DAC view names (e.g. `GIDataSource` and `PresentationDataSourceLink` were renamed
368
+ from `Columns` / `DataSourceLinks`). Pull the live names from
369
+ `GET {entity_base}/swagger.json` if unsure.
370
+ - Endpoint field names are display-based (`DefinitionCode` not `DefinitionCD`,
371
+ `VisibleinReport` not `IsVisible`). String-list fields take the **label**
372
+ (`ReportType` = `"Balance Sheet"`, `"Custom"`).
373
+ - Example: `get_entity("FLRTReportDefinition", filter="DefinitionCode eq 'BALANCE_SHEET'", expand="LineItems")`.
374
+ - Process actions exist on some entities (e.g. `GenerateReport`, `DetectColumns`)
375
+ — call via `invoke_action`.
376
+ - Security: `FLRTTenantCredentials` may expose secret fields (`ClientSecret`,
377
+ `Password`) over REST — drop those from the endpoint if not needed.
378
+ - Web Service Endpoints are editable in the SM207060 UI and, on newer builds, via
379
+ the `WebServiceEndpoints` entity (see `extend_endpoint` / `get_endpoint_definition`).
380
+ The entity is a projection of the wizard-driven form, so complex contracts are
381
+ still more reliably built in the UI.