@stubbedev/trimit-mcp 0.1.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.
- package/LICENSE +21 -0
- package/README.md +230 -0
- package/dist/categories/customers.js +187 -0
- package/dist/categories/exported.js +105 -0
- package/dist/categories/index.js +9 -0
- package/dist/categories/inventory.js +63 -0
- package/dist/categories/masterdata.js +246 -0
- package/dist/categories/metadata.js +27 -0
- package/dist/categories/postedsales.js +155 -0
- package/dist/categories/products.js +176 -0
- package/dist/categories/salesdocs.js +395 -0
- package/dist/categories/standard.js +228 -0
- package/dist/common.js +91 -0
- package/dist/index.js +10 -0
- package/dist/registry.js +27 -0
- package/dist/server.js +706 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 stubbedev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# TRIMIT Dev MCP
|
|
2
|
+
|
|
3
|
+
An MCP server that helps you construct and validate [TRIMIT API](https://apidocs.trimit.com/) calls — the fashion/apparel ERP extension layered on Microsoft Dynamics 365 Business Central. No authentication required in the server itself.
|
|
4
|
+
|
|
5
|
+
Tools are loaded on demand by resource category. Ask about sales orders and the salesdocs tools appear. Ask about masters and the products tools appear. The server starts lean and grows with your needs, and always knows the OAuth setup, URL templating, and OData conventions for every operation.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
- Constructs valid TRIMIT / Business Central API request URLs, methods, headers, and bodies
|
|
10
|
+
- Validates required and optional parameters via Zod schemas
|
|
11
|
+
- Returns the OAuth 2.0 / Microsoft Entra setup for every operation
|
|
12
|
+
- Returns ready-to-use fetch code examples
|
|
13
|
+
- Links to official documentation
|
|
14
|
+
- Explains cross-cutting concepts: OAuth auth, base URL templating, OData queries, ETag concurrency, `$batch`, paging, sales document lifecycle
|
|
15
|
+
- Loads resource categories on demand — only what you need
|
|
16
|
+
|
|
17
|
+
## Tools
|
|
18
|
+
|
|
19
|
+
### Always available
|
|
20
|
+
|
|
21
|
+
These tools are loaded immediately — no setup required.
|
|
22
|
+
|
|
23
|
+
| Tool | Description |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `list_categories` | List all resource categories and which are currently loaded |
|
|
26
|
+
| `load_category` | Load tools for a category; triggers `tools/list_changed` |
|
|
27
|
+
| `search_trimit_api` | Search TRIMIT API endpoints by keyword; returns a `suggestedCategory` to load |
|
|
28
|
+
| `trimit_build_batch` | Build a valid `/$batch` request body from any number of operations |
|
|
29
|
+
| `trimit_explain_auth` | OAuth 2.0 client credentials against Microsoft Entra |
|
|
30
|
+
| `trimit_explain_base_urls` | Base URL structure; `{tenant}`, `{environment}`, `{companyId}` substitution |
|
|
31
|
+
| `trimit_explain_odata` | `$filter`, `$select`, `$expand`, `$orderby`, `$top`, `$count` with examples |
|
|
32
|
+
| `trimit_explain_paging` | `@odata.nextLink` iteration and the BC 20 000-row page cap |
|
|
33
|
+
| `trimit_explain_concurrency` | `@odata.etag`, `If-Match`, `IEEE754Compatible` decimals |
|
|
34
|
+
| `trimit_explain_batch` | OData `$batch` envelope, `dependsOn`, response matching |
|
|
35
|
+
| `trimit_explain_doc_lifecycle` | Sales Import Journal → processed → posted; `exportedDocuments` markers |
|
|
36
|
+
| `trimit_explain_errors` | 400 / 401 / 403 / 404 / 409 / 412 / 429 and BC error codes |
|
|
37
|
+
|
|
38
|
+
### Resource categories (loaded on demand)
|
|
39
|
+
|
|
40
|
+
| Category | Tools | What you get |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| **standard** | 8 | Microsoft BC standard API: companies (incl. ODataV4 view), items, customers (GET + PATCH defaultDimensions), `$metadata` (API + ODataV4), entityDefinitions |
|
|
43
|
+
| **masterdata** | 8 | Campaigns, customerPriceGroups, priceGroupParameters, VarDimCombinations, vardimtypes, vardimtypevalues, itemAttributes, collections |
|
|
44
|
+
| **products** | 4 | TRIMIT Masters (style headers), Items (SKUs), Products feed, Categories — all with full expand defaults |
|
|
45
|
+
| **inventory** | 2 | Locations, Inventories (incl. Future Delivers) |
|
|
46
|
+
| **customers** | 5 | TRIMIT-enriched customers list, contacts, salespersons, POST customer, PATCH customer (additionalFields, If-Match, IEEE754Compatible) |
|
|
47
|
+
| **salesdocs** | 11 | Quote / Order / Invoice / Credit Memo / Blanket Order / Return Order — list, list-processed, get-by-systemId, typed lists (orders/invoices/credit memos/return orders), POST return order, POST sales document (create + add-fields), `$batch` bulk insert |
|
|
48
|
+
| **postedsales** | 5 | Posted documents (union), posted invoices, posted credit memos, posted return receipts, posted shipments (with tracking lines) |
|
|
49
|
+
| **exported** | 3 | GET / POST / DELETE `exportedDocuments` markers — exclude already-processed docs from future polls |
|
|
50
|
+
| **metadata** | 1 | TRIMIT integration API `$metadata` (EDMX) for codegen |
|
|
51
|
+
|
|
52
|
+
### Example tool output
|
|
53
|
+
|
|
54
|
+
Every tool returns a structured object with the full request details and auth requirements:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"endpoint": "https://api.businesscentral.dynamics.com/v2.0/{tenant}/{environment}/api/trimit/integration/v1.1/companies({companyId})/masters?$expand=masterDescriptions,masterDefDims,...",
|
|
59
|
+
"method": "GET",
|
|
60
|
+
"headers": {
|
|
61
|
+
"Authorization": "Bearer {token}",
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
"Accept": "application/json"
|
|
64
|
+
},
|
|
65
|
+
"pathParams": { "tenant": "{tenant}", "environment": "{environment}", "companyId": "{companyId}" },
|
|
66
|
+
"queryParams": { "$expand": "masterDescriptions,masterDefDims,..." },
|
|
67
|
+
"body": null,
|
|
68
|
+
"description": "TRIMIT Masters (style headers).",
|
|
69
|
+
"docsUrl": "https://apidocs.trimit.com/",
|
|
70
|
+
"codeExample": "const response = await fetch('...', { method: 'GET', headers: { Authorization: 'Bearer {token}' } });\nconst data = await response.json();",
|
|
71
|
+
"auth": {
|
|
72
|
+
"type": "OAuth 2.0 — Microsoft Entra (Azure AD) client credentials",
|
|
73
|
+
"tokenEndpoint": "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token",
|
|
74
|
+
"scope": "https://api.businesscentral.dynamics.com/.default",
|
|
75
|
+
"notes": "Customer-tenant Entra app registration with Business Central / TRIMIT integration consent."
|
|
76
|
+
},
|
|
77
|
+
"notes": "Key fields: number (master code), noSystem (SKU template), masterItems → SKU variants. Heavy payload — narrow with $select and trimmed $expand for production traffic."
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
The recommended way to run this server is via `npx` — no local install needed.
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
npx -y @stubbedev/trimit-mcp
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Claude Desktop
|
|
92
|
+
|
|
93
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"mcpServers": {
|
|
98
|
+
"trimit-dev": {
|
|
99
|
+
"command": "npx",
|
|
100
|
+
"args": ["-y", "@stubbedev/trimit-mcp"]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Claude Code (CLI)
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
claude mcp add trimit-dev -- npx -y @stubbedev/trimit-mcp
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Or add to your project's `.mcp.json`:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"mcpServers": {
|
|
117
|
+
"trimit-dev": {
|
|
118
|
+
"command": "npx",
|
|
119
|
+
"args": ["-y", "@stubbedev/trimit-mcp"]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Cursor
|
|
126
|
+
|
|
127
|
+
Open **Settings → MCP** and add a new server, or edit `~/.cursor/mcp.json`:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"mcpServers": {
|
|
132
|
+
"trimit-dev": {
|
|
133
|
+
"command": "npx",
|
|
134
|
+
"args": ["-y", "@stubbedev/trimit-mcp"]
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Windsurf
|
|
141
|
+
|
|
142
|
+
Edit `~/.codeium/windsurf/mcp_config.json`:
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"mcpServers": {
|
|
147
|
+
"trimit-dev": {
|
|
148
|
+
"command": "npx",
|
|
149
|
+
"args": ["-y", "@stubbedev/trimit-mcp"]
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Zed
|
|
156
|
+
|
|
157
|
+
Edit your `settings.json` (open via **Zed → Settings → Open Settings**):
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"context_servers": {
|
|
162
|
+
"trimit-dev": {
|
|
163
|
+
"command": {
|
|
164
|
+
"path": "npx",
|
|
165
|
+
"args": ["-y", "@stubbedev/trimit-mcp"]
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### OpenCode
|
|
173
|
+
|
|
174
|
+
Edit `~/.config/opencode/config.json`:
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"mcp": {
|
|
179
|
+
"trimit-dev": {
|
|
180
|
+
"type": "local",
|
|
181
|
+
"command": ["npx", "-y", "@stubbedev/trimit-mcp"]
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Codex (OpenAI)
|
|
188
|
+
|
|
189
|
+
Edit `~/.codex/config.json`:
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"mcpServers": {
|
|
194
|
+
"trimit-dev": {
|
|
195
|
+
"command": "npx",
|
|
196
|
+
"args": ["-y", "@stubbedev/trimit-mcp"]
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
git clone https://github.com/stubbedev/trimit-mcp.git
|
|
208
|
+
cd trimit-mcp
|
|
209
|
+
npm install
|
|
210
|
+
npm run build
|
|
211
|
+
npm start
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
For live reload during development:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
npm run dev
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Test with MCP Inspector
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
npx @modelcontextprotocol/inspector npx -y @stubbedev/trimit-mcp
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { TRIMIT_AUTH, buildHeaders, buildOdataQuery, fetchExample, trimitBase } from "../common.js";
|
|
3
|
+
const CATEGORY = "customers";
|
|
4
|
+
const CUSTOMERS_DEFAULT_EXPAND = [
|
|
5
|
+
"picture",
|
|
6
|
+
"defaultDimensions",
|
|
7
|
+
"priceGroupParameters",
|
|
8
|
+
"contactsInformation",
|
|
9
|
+
"customerFinancialDetail",
|
|
10
|
+
"configuredFields",
|
|
11
|
+
].join(",");
|
|
12
|
+
export const customersTools = [
|
|
13
|
+
{
|
|
14
|
+
name: "trimit_customers_list",
|
|
15
|
+
description: "List TRIMIT-enriched customers (picture, priceGroupParameters, contactsInformation, customerFinancialDetail, defaultDimensions, configuredFields).",
|
|
16
|
+
category: CATEGORY,
|
|
17
|
+
zodShape: {
|
|
18
|
+
filter: z.string().optional(),
|
|
19
|
+
select: z.array(z.string()).optional(),
|
|
20
|
+
expand: z.string().optional().describe(`Default expand: ${CUSTOMERS_DEFAULT_EXPAND}`),
|
|
21
|
+
top: z.number().optional(),
|
|
22
|
+
skip: z.number().optional(),
|
|
23
|
+
},
|
|
24
|
+
handler: (args) => {
|
|
25
|
+
const expand = args.expand ?? CUSTOMERS_DEFAULT_EXPAND;
|
|
26
|
+
const { qs, params } = buildOdataQuery({ ...args, expand });
|
|
27
|
+
const endpoint = `${trimitBase()}/customers${qs}`;
|
|
28
|
+
return {
|
|
29
|
+
endpoint,
|
|
30
|
+
method: "GET",
|
|
31
|
+
headers: buildHeaders(),
|
|
32
|
+
pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
|
|
33
|
+
queryParams: params,
|
|
34
|
+
body: null,
|
|
35
|
+
description: "TRIMIT customers list.",
|
|
36
|
+
docsUrl: "https://apidocs.trimit.com/",
|
|
37
|
+
codeExample: fetchExample(endpoint, "GET", null),
|
|
38
|
+
auth: TRIMIT_AUTH,
|
|
39
|
+
notes: null,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "trimit_customers_get_contacts",
|
|
45
|
+
description: "List BC Contacts with picture expanded.",
|
|
46
|
+
category: CATEGORY,
|
|
47
|
+
zodShape: {
|
|
48
|
+
filter: z.string().optional(),
|
|
49
|
+
select: z.array(z.string()).optional(),
|
|
50
|
+
expand: z.string().optional().describe("Default: 'picture'"),
|
|
51
|
+
top: z.number().optional(),
|
|
52
|
+
skip: z.number().optional(),
|
|
53
|
+
},
|
|
54
|
+
handler: (args) => {
|
|
55
|
+
const expand = args.expand ?? "picture";
|
|
56
|
+
const { qs, params } = buildOdataQuery({ ...args, expand });
|
|
57
|
+
const endpoint = `${trimitBase()}/contacts${qs}`;
|
|
58
|
+
return {
|
|
59
|
+
endpoint,
|
|
60
|
+
method: "GET",
|
|
61
|
+
headers: buildHeaders(),
|
|
62
|
+
pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
|
|
63
|
+
queryParams: params,
|
|
64
|
+
body: null,
|
|
65
|
+
description: "Contacts.",
|
|
66
|
+
docsUrl: "https://apidocs.trimit.com/",
|
|
67
|
+
codeExample: fetchExample(endpoint, "GET", null),
|
|
68
|
+
auth: TRIMIT_AUTH,
|
|
69
|
+
notes: "Fields: companyName, addressLine1/2, city, salespersonCode, phoneNo, email, …",
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "trimit_customers_get_salespersons",
|
|
75
|
+
description: "List salespersons (returns {code, name, email, phoneNo, commissionPercent, globalDimension1Code, globalDimension2Code}).",
|
|
76
|
+
category: CATEGORY,
|
|
77
|
+
zodShape: {
|
|
78
|
+
filter: z.string().optional(),
|
|
79
|
+
select: z.array(z.string()).optional(),
|
|
80
|
+
top: z.number().optional(),
|
|
81
|
+
skip: z.number().optional(),
|
|
82
|
+
},
|
|
83
|
+
handler: (args) => {
|
|
84
|
+
const { qs, params } = buildOdataQuery(args);
|
|
85
|
+
const endpoint = `${trimitBase()}/salespersons${qs}`;
|
|
86
|
+
return {
|
|
87
|
+
endpoint,
|
|
88
|
+
method: "GET",
|
|
89
|
+
headers: buildHeaders(),
|
|
90
|
+
pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
|
|
91
|
+
queryParams: params,
|
|
92
|
+
body: null,
|
|
93
|
+
description: "Salespersons list.",
|
|
94
|
+
docsUrl: "https://apidocs.trimit.com/",
|
|
95
|
+
codeExample: fetchExample(endpoint, "GET", null),
|
|
96
|
+
auth: TRIMIT_AUTH,
|
|
97
|
+
notes: null,
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "trimit_customers_create",
|
|
103
|
+
description: "Create a TRIMIT customer. 201 on success. 409 if customer number already exists.",
|
|
104
|
+
category: CATEGORY,
|
|
105
|
+
zodShape: {
|
|
106
|
+
number: z.string().describe("Customer number (unique)"),
|
|
107
|
+
displayName: z.string(),
|
|
108
|
+
type: z.string().describe("e.g. 'Company' or 'Person'"),
|
|
109
|
+
addressLine1: z.string(),
|
|
110
|
+
city: z.string(),
|
|
111
|
+
country: z.string().describe("Country/region code (ISO)"),
|
|
112
|
+
postalCode: z.string(),
|
|
113
|
+
email: z.string(),
|
|
114
|
+
phoneNumber: z.string().optional(),
|
|
115
|
+
addressLine2: z.string().optional(),
|
|
116
|
+
state: z.string().optional(),
|
|
117
|
+
website: z.string().optional(),
|
|
118
|
+
salespersonCode: z.string().optional(),
|
|
119
|
+
salespersonCode2: z.string().optional(),
|
|
120
|
+
salespersonCode3: z.string().optional(),
|
|
121
|
+
creditLimit: z.number().optional(),
|
|
122
|
+
blocked: z.string().optional().describe(" ' ', 'Ship', 'Invoice', 'All'"),
|
|
123
|
+
chain: z.string().optional(),
|
|
124
|
+
companyGroup: z.string().optional(),
|
|
125
|
+
selltoGroup: z.string().optional(),
|
|
126
|
+
selltoType: z.string().optional(),
|
|
127
|
+
commissionGroup: z.string().optional(),
|
|
128
|
+
bonusGroup: z.string().optional(),
|
|
129
|
+
allocationPriority: z.number().optional(),
|
|
130
|
+
additionalFields: z
|
|
131
|
+
.array(z.object({ name: z.string(), value: z.string() }))
|
|
132
|
+
.optional()
|
|
133
|
+
.describe("Custom configuredFields"),
|
|
134
|
+
},
|
|
135
|
+
handler: (args) => {
|
|
136
|
+
const endpoint = `${trimitBase()}/customers`;
|
|
137
|
+
const body = Object.fromEntries(Object.entries(args).filter(([, v]) => v !== undefined));
|
|
138
|
+
return {
|
|
139
|
+
endpoint,
|
|
140
|
+
method: "POST",
|
|
141
|
+
headers: buildHeaders(),
|
|
142
|
+
pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
|
|
143
|
+
queryParams: {},
|
|
144
|
+
body,
|
|
145
|
+
description: "Create a new customer.",
|
|
146
|
+
docsUrl: "https://apidocs.trimit.com/",
|
|
147
|
+
codeExample: fetchExample(endpoint, "POST", body),
|
|
148
|
+
auth: TRIMIT_AUTH,
|
|
149
|
+
notes: "201 returns the created customer with @odata.etag + systemId. 409 'EntityWithSameKeyExists' means number is taken. additionalFields targets BC configuredFields table.",
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "trimit_customers_patch",
|
|
155
|
+
description: "Patch a TRIMIT customer (typically additionalFields). Sends If-Match for OData concurrency and IEEE754Compatible: true for decimal handling.",
|
|
156
|
+
category: CATEGORY,
|
|
157
|
+
zodShape: {
|
|
158
|
+
customerId: z.string().describe("Customer systemId GUID"),
|
|
159
|
+
ifMatch: z.string().default("*").describe("ETag from a prior GET, or '*' for unconditional"),
|
|
160
|
+
additionalFields: z
|
|
161
|
+
.array(z.object({ name: z.string(), value: z.string() }))
|
|
162
|
+
.optional()
|
|
163
|
+
.describe("Replacement set of additionalFields"),
|
|
164
|
+
patch: z.record(z.string(), z.unknown()).optional().describe("Other fields to PATCH (e.g. { creditLimit: 5000 })"),
|
|
165
|
+
},
|
|
166
|
+
handler: (args) => {
|
|
167
|
+
const endpoint = `${trimitBase()}/customers(${args.customerId})?$expand=additionalFields`;
|
|
168
|
+
const body = { ...(args.patch ?? {}) };
|
|
169
|
+
if (args.additionalFields)
|
|
170
|
+
body.additionalFields = args.additionalFields;
|
|
171
|
+
const extraHeaders = { "If-Match": args.ifMatch ?? "*", "IEEE754Compatible": "true" };
|
|
172
|
+
return {
|
|
173
|
+
endpoint,
|
|
174
|
+
method: "PATCH",
|
|
175
|
+
headers: buildHeaders(extraHeaders),
|
|
176
|
+
pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}", customerId: args.customerId },
|
|
177
|
+
queryParams: { $expand: "additionalFields" },
|
|
178
|
+
body,
|
|
179
|
+
description: "Patch a customer.",
|
|
180
|
+
docsUrl: "https://apidocs.trimit.com/",
|
|
181
|
+
codeExample: fetchExample(endpoint, "PATCH", body, extraHeaders),
|
|
182
|
+
auth: TRIMIT_AUTH,
|
|
183
|
+
notes: "If-Match: '*' bypasses optimistic concurrency. Use the @odata.etag value from a prior GET for safe concurrent updates. IEEE754Compatible: true forces decimals to be sent/received as JSON strings (BC convention) to avoid precision loss.",
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
];
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { TRIMIT_AUTH, buildHeaders, fetchExample, trimitBase } from "../common.js";
|
|
3
|
+
const CATEGORY = "exported";
|
|
4
|
+
const DOC_TYPES = [
|
|
5
|
+
"Quote",
|
|
6
|
+
"Order",
|
|
7
|
+
"Invoice",
|
|
8
|
+
"Credit Memo",
|
|
9
|
+
"Blanket Order",
|
|
10
|
+
"Return Order",
|
|
11
|
+
"Posted Invoice",
|
|
12
|
+
"Posted Credit Memo",
|
|
13
|
+
"Posted Shipment",
|
|
14
|
+
"Posted Return Receipt",
|
|
15
|
+
];
|
|
16
|
+
export const exportedTools = [
|
|
17
|
+
{
|
|
18
|
+
name: "trimit_exported_get",
|
|
19
|
+
description: "Check whether a specific document has been marked exported (and is therefore excluded from default sales document polls).",
|
|
20
|
+
category: CATEGORY,
|
|
21
|
+
zodShape: {
|
|
22
|
+
documentType: z.enum(DOC_TYPES).describe("Case-sensitive doc type"),
|
|
23
|
+
documentNumber: z.string(),
|
|
24
|
+
},
|
|
25
|
+
handler: (args) => {
|
|
26
|
+
const endpoint = `${trimitBase()}/exportedDocuments('${encodeURIComponent(args.documentType)}','${encodeURIComponent(args.documentNumber)}')`;
|
|
27
|
+
return {
|
|
28
|
+
endpoint,
|
|
29
|
+
method: "GET",
|
|
30
|
+
headers: buildHeaders(),
|
|
31
|
+
pathParams: {
|
|
32
|
+
tenant: "{tenant}",
|
|
33
|
+
environment: "{environment}",
|
|
34
|
+
companyId: "{companyId}",
|
|
35
|
+
documentType: args.documentType,
|
|
36
|
+
documentNumber: args.documentNumber,
|
|
37
|
+
},
|
|
38
|
+
queryParams: {},
|
|
39
|
+
body: null,
|
|
40
|
+
description: "Lookup an exported document marker.",
|
|
41
|
+
docsUrl: "https://apidocs.trimit.com/",
|
|
42
|
+
codeExample: fetchExample(endpoint, "GET", null),
|
|
43
|
+
auth: TRIMIT_AUTH,
|
|
44
|
+
notes: "404 means the doc has not been marked exported. documentType is case-sensitive — match the doc-type string exactly.",
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "trimit_exported_create",
|
|
50
|
+
description: "Mark a document as exported. After this, subsequent GET /salesDocuments calls will exclude the doc (until it is un-marked).",
|
|
51
|
+
category: CATEGORY,
|
|
52
|
+
zodShape: {
|
|
53
|
+
type: z.enum(DOC_TYPES),
|
|
54
|
+
number: z.string(),
|
|
55
|
+
},
|
|
56
|
+
handler: (args) => {
|
|
57
|
+
const endpoint = `${trimitBase()}/exportedDocuments`;
|
|
58
|
+
const body = { type: args.type, number: args.number };
|
|
59
|
+
return {
|
|
60
|
+
endpoint,
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: buildHeaders(),
|
|
63
|
+
pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
|
|
64
|
+
queryParams: {},
|
|
65
|
+
body,
|
|
66
|
+
description: "Mark a document exported.",
|
|
67
|
+
docsUrl: "https://apidocs.trimit.com/",
|
|
68
|
+
codeExample: fetchExample(endpoint, "POST", body),
|
|
69
|
+
auth: TRIMIT_AUTH,
|
|
70
|
+
notes: "One call per document — there is no batch variant. Idempotent: re-marking an already-exported doc returns the existing record.",
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "trimit_exported_delete",
|
|
76
|
+
description: "Un-mark exported (error recovery — re-includes the doc in future polls). Returns 204.",
|
|
77
|
+
category: CATEGORY,
|
|
78
|
+
zodShape: {
|
|
79
|
+
documentType: z.enum(DOC_TYPES),
|
|
80
|
+
documentNumber: z.string(),
|
|
81
|
+
},
|
|
82
|
+
handler: (args) => {
|
|
83
|
+
const endpoint = `${trimitBase()}/exportedDocuments('${encodeURIComponent(args.documentType)}','${encodeURIComponent(args.documentNumber)}')`;
|
|
84
|
+
return {
|
|
85
|
+
endpoint,
|
|
86
|
+
method: "DELETE",
|
|
87
|
+
headers: buildHeaders(),
|
|
88
|
+
pathParams: {
|
|
89
|
+
tenant: "{tenant}",
|
|
90
|
+
environment: "{environment}",
|
|
91
|
+
companyId: "{companyId}",
|
|
92
|
+
documentType: args.documentType,
|
|
93
|
+
documentNumber: args.documentNumber,
|
|
94
|
+
},
|
|
95
|
+
queryParams: {},
|
|
96
|
+
body: null,
|
|
97
|
+
description: "Delete an exported-document marker.",
|
|
98
|
+
docsUrl: "https://apidocs.trimit.com/",
|
|
99
|
+
codeExample: fetchExample(endpoint, "DELETE", null),
|
|
100
|
+
auth: TRIMIT_AUTH,
|
|
101
|
+
notes: "204 No Content on success. Use to re-import a doc you previously marked exported by mistake.",
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { standardTools } from "./standard.js";
|
|
2
|
+
export { masterdataTools } from "./masterdata.js";
|
|
3
|
+
export { productsTools } from "./products.js";
|
|
4
|
+
export { inventoryTools } from "./inventory.js";
|
|
5
|
+
export { customersTools } from "./customers.js";
|
|
6
|
+
export { salesdocsTools } from "./salesdocs.js";
|
|
7
|
+
export { postedsalesTools } from "./postedsales.js";
|
|
8
|
+
export { exportedTools } from "./exported.js";
|
|
9
|
+
export { metadataTools } from "./metadata.js";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { TRIMIT_AUTH, buildHeaders, buildOdataQuery, fetchExample, trimitBase } from "../common.js";
|
|
3
|
+
const CATEGORY = "inventory";
|
|
4
|
+
export const inventoryTools = [
|
|
5
|
+
{
|
|
6
|
+
name: "trimit_inventory_get_locations",
|
|
7
|
+
description: "List warehouse locations.",
|
|
8
|
+
category: CATEGORY,
|
|
9
|
+
zodShape: {
|
|
10
|
+
filter: z.string().optional(),
|
|
11
|
+
select: z.array(z.string()).optional(),
|
|
12
|
+
top: z.number().optional(),
|
|
13
|
+
skip: z.number().optional(),
|
|
14
|
+
},
|
|
15
|
+
handler: (args) => {
|
|
16
|
+
const { qs, params } = buildOdataQuery(args);
|
|
17
|
+
const endpoint = `${trimitBase()}/locations${qs}`;
|
|
18
|
+
return {
|
|
19
|
+
endpoint,
|
|
20
|
+
method: "GET",
|
|
21
|
+
headers: buildHeaders(),
|
|
22
|
+
pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
|
|
23
|
+
queryParams: params,
|
|
24
|
+
body: null,
|
|
25
|
+
description: "Warehouse / location master.",
|
|
26
|
+
docsUrl: "https://apidocs.trimit.com/",
|
|
27
|
+
codeExample: fetchExample(endpoint, "GET", null),
|
|
28
|
+
auth: TRIMIT_AUTH,
|
|
29
|
+
notes: null,
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "trimit_inventory_get_inventories",
|
|
35
|
+
description: "Get inventory / availability per (item, location). When TRIMIT 'Export Future Delivers' is enabled, includes pre-order stock.",
|
|
36
|
+
category: CATEGORY,
|
|
37
|
+
zodShape: {
|
|
38
|
+
filter: z.string().optional().describe("e.g. \"itemNo eq '20002036'\" or filter on locationCode"),
|
|
39
|
+
select: z.array(z.string()).optional(),
|
|
40
|
+
top: z.number().optional(),
|
|
41
|
+
skip: z.number().optional(),
|
|
42
|
+
readOnly: z.boolean().optional().describe("Send Data-Access-Intent: ReadOnly. Default true."),
|
|
43
|
+
},
|
|
44
|
+
handler: (args) => {
|
|
45
|
+
const { qs, params } = buildOdataQuery(args);
|
|
46
|
+
const endpoint = `${trimitBase()}/inventories${qs}`;
|
|
47
|
+
const extra = args.readOnly !== false ? { "Data-Access-Intent": "ReadOnly" } : undefined;
|
|
48
|
+
return {
|
|
49
|
+
endpoint,
|
|
50
|
+
method: "GET",
|
|
51
|
+
headers: buildHeaders(extra),
|
|
52
|
+
pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
|
|
53
|
+
queryParams: params,
|
|
54
|
+
body: null,
|
|
55
|
+
description: "Inventory availability per (item, location).",
|
|
56
|
+
docsUrl: "https://apidocs.trimit.com/",
|
|
57
|
+
codeExample: fetchExample(endpoint, "GET", null, extra),
|
|
58
|
+
auth: TRIMIT_AUTH,
|
|
59
|
+
notes: "For frequent inventory polling prefer narrow filters + Data-Access-Intent: ReadOnly. Heavy aggregations should use $top/$skip pagination.",
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
];
|