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.
- grp_mcp-0.2.0/.env.example +30 -0
- grp_mcp-0.2.0/.gitignore +25 -0
- grp_mcp-0.2.0/How-to-start-webapp-change-instance.txt +2 -0
- grp_mcp-0.2.0/PKG-INFO +381 -0
- grp_mcp-0.2.0/README.md +367 -0
- grp_mcp-0.2.0/connections.example.json +34 -0
- grp_mcp-0.2.0/playwright/EXTENDING_ENDPOINTS.md +192 -0
- grp_mcp-0.2.0/playwright/add_endpoint_action.js +121 -0
- grp_mcp-0.2.0/playwright/add_endpoint_action_modern.js +124 -0
- grp_mcp-0.2.0/playwright/add_endpoint_entity.js +167 -0
- grp_mcp-0.2.0/playwright/add_endpoint_entity_modern.js +146 -0
- grp_mcp-0.2.0/playwright/change_company_type_modern.js +54 -0
- grp_mcp-0.2.0/playwright/delete_endpoint_entity_modern.js +46 -0
- grp_mcp-0.2.0/playwright/delete_finyear.js +39 -0
- grp_mcp-0.2.0/playwright/dump_full_schema.py +113 -0
- grp_mcp-0.2.0/playwright/inspect_company_fields.js +53 -0
- grp_mcp-0.2.0/playwright/inspect_grid_fields.js +50 -0
- grp_mcp-0.2.0/playwright/inspect_screen_fields.js +43 -0
- grp_mcp-0.2.0/playwright/inspect_segkey.js +41 -0
- grp_mcp-0.2.0/playwright/inspect_sm207060.js +36 -0
- grp_mcp-0.2.0/playwright/inspect_tabs_cs101500.js +72 -0
- grp_mcp-0.2.0/playwright/populate_all_views.js +104 -0
- grp_mcp-0.2.0/playwright/set_features_modern.js +77 -0
- grp_mcp-0.2.0/playwright/setup_finyear.js +120 -0
- grp_mcp-0.2.0/pyproject.toml +26 -0
- grp_mcp-0.2.0/src/grp_mcp/__init__.py +3 -0
- grp_mcp-0.2.0/src/grp_mcp/acumatica.py +505 -0
- grp_mcp-0.2.0/src/grp_mcp/config.py +157 -0
- grp_mcp-0.2.0/src/grp_mcp/customization.py +167 -0
- grp_mcp-0.2.0/src/grp_mcp/loaders.py +75 -0
- grp_mcp-0.2.0/src/grp_mcp/server.py +1436 -0
- 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
|
grp_mcp-0.2.0/.gitignore
ADDED
|
@@ -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/
|
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.
|