@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.
@@ -0,0 +1,395 @@
1
+ import { z } from "zod";
2
+ import { TRIMIT_AUTH, buildHeaders, buildOdataQuery, fetchExample, trimitBase } from "../common.js";
3
+ const CATEGORY = "salesdocs";
4
+ const DOC_TYPES = ["Quote", "Order", "Invoice", "Credit Memo", "Blanket Order", "Return Order"];
5
+ export const salesdocsTools = [
6
+ {
7
+ name: "trimit_salesdocs_list",
8
+ description: "List all sales documents currently in the TRIMIT Sales Import Journal (Quote/Order/Invoice/Credit Memo/Blanket Order/Return Order, both unprocessed and processed).",
9
+ category: CATEGORY,
10
+ zodShape: {
11
+ filter: z.string().optional(),
12
+ select: z.array(z.string()).optional(),
13
+ expand: z
14
+ .string()
15
+ .optional()
16
+ .describe("Default: 'salesDocumentLines($expand=additionalFields),additionalFields'"),
17
+ top: z.number().optional(),
18
+ skip: z.number().optional(),
19
+ },
20
+ handler: (args) => {
21
+ const expand = args.expand ?? "salesDocumentLines($expand=additionalFields),additionalFields";
22
+ const { qs, params } = buildOdataQuery({ ...args, expand });
23
+ const endpoint = `${trimitBase()}/salesDocuments${qs}`;
24
+ return {
25
+ endpoint,
26
+ method: "GET",
27
+ headers: buildHeaders(),
28
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
29
+ queryParams: params,
30
+ body: null,
31
+ description: "All open sales documents.",
32
+ docsUrl: "https://apidocs.trimit.com/",
33
+ codeExample: fetchExample(endpoint, "GET", null),
34
+ auth: TRIMIT_AUTH,
35
+ notes: "Use the exportedDocuments helpers (category 'exported') to mark a doc as exported and exclude it from this list on future polls.",
36
+ };
37
+ },
38
+ },
39
+ {
40
+ name: "trimit_salesdocs_list_processed",
41
+ description: "List sales documents that have been processed (imported into BC). Filters processedDate > 0001-01-01.",
42
+ category: CATEGORY,
43
+ zodShape: {
44
+ extraFilter: z.string().optional().describe("Additional OData $filter ANDed with the processed filter"),
45
+ expand: z.string().optional(),
46
+ top: z.number().optional(),
47
+ skip: z.number().optional(),
48
+ },
49
+ handler: (args) => {
50
+ const filter = args.extraFilter
51
+ ? `(processedDate gt 0001-01-01) and (${args.extraFilter})`
52
+ : `(processedDate gt 0001-01-01)`;
53
+ const expand = args.expand ?? "salesDocumentLines($expand=additionalFields),additionalFields";
54
+ const { qs, params } = buildOdataQuery({ filter, expand, top: args.top, skip: args.skip });
55
+ const endpoint = `${trimitBase()}/salesDocuments()${qs}`;
56
+ return {
57
+ endpoint,
58
+ method: "GET",
59
+ headers: buildHeaders(),
60
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
61
+ queryParams: params,
62
+ body: null,
63
+ description: "Sales documents that have been imported into BC.",
64
+ docsUrl: "https://apidocs.trimit.com/",
65
+ codeExample: fetchExample(endpoint, "GET", null),
66
+ auth: TRIMIT_AUTH,
67
+ notes: null,
68
+ };
69
+ },
70
+ },
71
+ {
72
+ name: "trimit_salesdocs_get_processed_one",
73
+ description: "Get one processed sales document by systemId.",
74
+ category: CATEGORY,
75
+ zodShape: {
76
+ systemId: z.string().describe("Document systemId (GUID)"),
77
+ expand: z.string().optional(),
78
+ },
79
+ handler: (args) => {
80
+ const expand = args.expand ?? "salesDocumentLines($expand=additionalFields),additionalFields";
81
+ const { qs, params } = buildOdataQuery({ expand });
82
+ const endpoint = `${trimitBase()}/salesDocuments(${args.systemId})${qs}`;
83
+ return {
84
+ endpoint,
85
+ method: "GET",
86
+ headers: buildHeaders(),
87
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}", systemId: args.systemId },
88
+ queryParams: params,
89
+ body: null,
90
+ description: "Single sales document by GUID.",
91
+ docsUrl: "https://apidocs.trimit.com/",
92
+ codeExample: fetchExample(endpoint, "GET", null),
93
+ auth: TRIMIT_AUTH,
94
+ notes: null,
95
+ };
96
+ },
97
+ },
98
+ {
99
+ name: "trimit_salesdocs_list_orders",
100
+ description: "List sales orders only.",
101
+ category: CATEGORY,
102
+ zodShape: {
103
+ filter: z.string().optional(),
104
+ expand: z.string().optional(),
105
+ top: z.number().optional(),
106
+ skip: z.number().optional(),
107
+ },
108
+ handler: (args) => {
109
+ const expand = args.expand ?? "salesOrderLines($expand=additionalFields),additionalFields";
110
+ const { qs, params } = buildOdataQuery({ ...args, expand });
111
+ const endpoint = `${trimitBase()}/salesOrders${qs}`;
112
+ return {
113
+ endpoint,
114
+ method: "GET",
115
+ headers: buildHeaders(),
116
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
117
+ queryParams: params,
118
+ body: null,
119
+ description: "Sales orders list.",
120
+ docsUrl: "https://apidocs.trimit.com/",
121
+ codeExample: fetchExample(endpoint, "GET", null),
122
+ auth: TRIMIT_AUTH,
123
+ notes: null,
124
+ };
125
+ },
126
+ },
127
+ {
128
+ name: "trimit_salesdocs_list_invoices",
129
+ description: "List unposted sales invoices.",
130
+ category: CATEGORY,
131
+ zodShape: {
132
+ filter: z.string().optional(),
133
+ expand: z.string().optional(),
134
+ top: z.number().optional(),
135
+ skip: z.number().optional(),
136
+ },
137
+ handler: (args) => {
138
+ const expand = args.expand ?? "salesInvoiceLines($expand=additionalFields),additionalFields";
139
+ const { qs, params } = buildOdataQuery({ ...args, expand });
140
+ const endpoint = `${trimitBase()}/salesInvoices${qs}`;
141
+ return {
142
+ endpoint,
143
+ method: "GET",
144
+ headers: buildHeaders(),
145
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
146
+ queryParams: params,
147
+ body: null,
148
+ description: "Unposted sales invoices.",
149
+ docsUrl: "https://apidocs.trimit.com/",
150
+ codeExample: fetchExample(endpoint, "GET", null),
151
+ auth: TRIMIT_AUTH,
152
+ notes: "Posted invoices live under category 'postedsales'.",
153
+ };
154
+ },
155
+ },
156
+ {
157
+ name: "trimit_salesdocs_list_credit_memos",
158
+ description: "List unposted sales credit memos.",
159
+ category: CATEGORY,
160
+ zodShape: {
161
+ filter: z.string().optional(),
162
+ expand: z.string().optional(),
163
+ top: z.number().optional(),
164
+ skip: z.number().optional(),
165
+ },
166
+ handler: (args) => {
167
+ const expand = args.expand ?? "salesCreditMemoLines($expand=additionalFields),additionalFields";
168
+ const { qs, params } = buildOdataQuery({ ...args, expand });
169
+ const endpoint = `${trimitBase()}/salesCreditMemos${qs}`;
170
+ return {
171
+ endpoint,
172
+ method: "GET",
173
+ headers: buildHeaders(),
174
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
175
+ queryParams: params,
176
+ body: null,
177
+ description: "Unposted credit memos.",
178
+ docsUrl: "https://apidocs.trimit.com/",
179
+ codeExample: fetchExample(endpoint, "GET", null),
180
+ auth: TRIMIT_AUTH,
181
+ notes: null,
182
+ };
183
+ },
184
+ },
185
+ {
186
+ name: "trimit_salesdocs_list_return_orders",
187
+ description: "List sales return orders. Only returns orders **created via this API** are visible here.",
188
+ category: CATEGORY,
189
+ zodShape: {
190
+ filter: z.string().optional(),
191
+ expand: z.string().optional(),
192
+ top: z.number().optional(),
193
+ skip: z.number().optional(),
194
+ },
195
+ handler: (args) => {
196
+ const expand = args.expand ?? "salesReturnOrderLines($expand=additionalFields),additionalFields";
197
+ const { qs, params } = buildOdataQuery({ ...args, expand });
198
+ const endpoint = `${trimitBase()}/salesReturnOrders${qs}`;
199
+ return {
200
+ endpoint,
201
+ method: "GET",
202
+ headers: buildHeaders(),
203
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
204
+ queryParams: params,
205
+ body: null,
206
+ description: "Sales return orders (API-created only).",
207
+ docsUrl: "https://apidocs.trimit.com/",
208
+ codeExample: fetchExample(endpoint, "GET", null),
209
+ auth: TRIMIT_AUTH,
210
+ notes: "Return orders created directly in the BC client are not surfaced here — by design. To see all posted return receipts use trimit_postedsales_list_return_receipts.",
211
+ };
212
+ },
213
+ },
214
+ {
215
+ name: "trimit_salesdocs_create_return_order",
216
+ description: "Create a sales return order.",
217
+ category: CATEGORY,
218
+ zodShape: {
219
+ returnOrderNo: z.string(),
220
+ sellToCustomerNo: z.string().optional(),
221
+ SellToCustomerName: z.string().optional(),
222
+ sellToEmail: z.string().optional(),
223
+ selltoPhoneNo: z.string().optional(),
224
+ orderDate: z.string().describe("YYYY-MM-DD"),
225
+ releaseDocument: z.boolean().optional(),
226
+ additionalFields: z
227
+ .array(z.object({ number: z.string().optional(), name: z.string(), value: z.string() }))
228
+ .optional(),
229
+ salesReturnOrderLines: z
230
+ .array(z.object({
231
+ lineNo: z.number(),
232
+ type: z.string().describe("Usually 'Item'"),
233
+ no: z.string().describe("Item number"),
234
+ unitPrice: z.number(),
235
+ quantity: z.number(),
236
+ locationCode: z.string().optional(),
237
+ additionalFields: z
238
+ .array(z.object({ name: z.string(), value: z.string() }))
239
+ .optional(),
240
+ }))
241
+ .min(1),
242
+ },
243
+ handler: (args) => {
244
+ const expand = "salesReturnOrderLines($expand=additionalFields),additionalFields";
245
+ const { qs, params } = buildOdataQuery({ expand });
246
+ const endpoint = `${trimitBase()}/salesReturnOrders${qs}`;
247
+ const body = Object.fromEntries(Object.entries(args).filter(([, v]) => v !== undefined));
248
+ return {
249
+ endpoint,
250
+ method: "POST",
251
+ headers: buildHeaders(),
252
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
253
+ queryParams: params,
254
+ body,
255
+ description: "Create a return order.",
256
+ docsUrl: "https://apidocs.trimit.com/",
257
+ codeExample: fetchExample(endpoint, "POST", body),
258
+ auth: TRIMIT_AUTH,
259
+ notes: "releaseDocument=true releases the return order in BC immediately. additionalFields on header and line write to configuredFields tables.",
260
+ };
261
+ },
262
+ },
263
+ {
264
+ name: "trimit_salesdocs_post_add_fields",
265
+ description: "Append lines / set additionalFields on an existing sales document. Variant of /salesDocuments POST that targets an already-existing docType+docNo.",
266
+ category: CATEGORY,
267
+ zodShape: {
268
+ docType: z.enum(DOC_TYPES),
269
+ docNo: z.string(),
270
+ SellToCustomerName: z.string().optional(),
271
+ orderDate: z.string().optional().describe("YYYY-MM-DD"),
272
+ releaseDocument: z.boolean().optional(),
273
+ additionalFields: z
274
+ .array(z.object({ name: z.string(), value: z.string() }))
275
+ .optional(),
276
+ salesDocumentLines: z
277
+ .array(z.object({
278
+ lineNo: z.number().optional(),
279
+ type: z.string().describe("'Item', 'G/L Account', etc."),
280
+ no: z.string(),
281
+ unitPrice: z.number().optional(),
282
+ quantity: z.number().optional(),
283
+ unitOfMeasureCode: z.string().optional(),
284
+ locationCode: z.string().optional(),
285
+ periodCode: z.string().optional(),
286
+ discountAmount: z.number().optional(),
287
+ additionalFields: z.array(z.object({ name: z.string(), value: z.string() })).optional(),
288
+ }))
289
+ .optional(),
290
+ },
291
+ handler: (args) => {
292
+ const endpoint = `${trimitBase()}/salesDocuments`;
293
+ const body = Object.fromEntries(Object.entries(args).filter(([, v]) => v !== undefined));
294
+ return {
295
+ endpoint,
296
+ method: "POST",
297
+ headers: buildHeaders(),
298
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
299
+ queryParams: {},
300
+ body,
301
+ description: "Append fields/lines to an existing sales document.",
302
+ docsUrl: "https://apidocs.trimit.com/",
303
+ codeExample: fetchExample(endpoint, "POST", body),
304
+ auth: TRIMIT_AUTH,
305
+ notes: "Same endpoint URL as create — the server distinguishes 'add fields' vs 'create' by whether docNo already exists. Use trimit_salesdocs_create for new docs to keep intent explicit.",
306
+ };
307
+ },
308
+ },
309
+ {
310
+ name: "trimit_salesdocs_create",
311
+ description: "Create a new sales document (Quote / Order / Invoice / Credit Memo / Blanket Order / Return Order) with one or more lines.",
312
+ category: CATEGORY,
313
+ zodShape: {
314
+ docType: z.enum(DOC_TYPES),
315
+ docNo: z.string().describe("Customer-facing document number (unique per docType)"),
316
+ sellToCustomerNo: z.string().describe("BC customer number, e.g. '10000'"),
317
+ orderDate: z.string().describe("YYYY-MM-DD"),
318
+ releaseDocument: z.boolean().optional().describe("Release the doc in BC on create."),
319
+ additionalFields: z
320
+ .array(z.object({ name: z.string(), value: z.string() }))
321
+ .optional(),
322
+ salesDocumentLines: z
323
+ .array(z.object({
324
+ type: z.string().describe("'Item', 'G/L Account', 'Resource', 'Fixed Asset', 'Charge (Item)'"),
325
+ no: z.string().describe("Item number / G/L account / etc."),
326
+ unitPrice: z.number(),
327
+ quantity: z.number(),
328
+ unitOfMeasureCode: z.string().optional(),
329
+ locationCode: z.string().optional(),
330
+ periodCode: z.string().optional(),
331
+ discountAmount: z.number().optional(),
332
+ additionalFields: z
333
+ .array(z.object({ name: z.string(), value: z.string() }))
334
+ .optional(),
335
+ }))
336
+ .min(1),
337
+ },
338
+ handler: (args) => {
339
+ const endpoint = `${trimitBase()}/salesDocuments`;
340
+ const body = Object.fromEntries(Object.entries(args).filter(([, v]) => v !== undefined));
341
+ return {
342
+ endpoint,
343
+ method: "POST",
344
+ headers: buildHeaders(),
345
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
346
+ queryParams: {},
347
+ body,
348
+ description: "Create a sales document.",
349
+ docsUrl: "https://apidocs.trimit.com/",
350
+ codeExample: fetchExample(endpoint, "POST", body),
351
+ auth: TRIMIT_AUTH,
352
+ notes: "201 returns the created entity with @odata.etag and systemId. Use trimit_salesdocs_post_lines_batch to push many docs in a single round-trip.",
353
+ };
354
+ },
355
+ },
356
+ {
357
+ name: "trimit_salesdocs_post_lines_batch",
358
+ description: "Bulk-create sales documents / lines via OData $batch. Pass any number of POST requests; one round-trip.",
359
+ category: CATEGORY,
360
+ zodShape: {
361
+ requests: z
362
+ .array(z.object({
363
+ method: z.literal("POST"),
364
+ url: z.string().describe("Relative URL, e.g. companies({id})/salesDocumentLines"),
365
+ body: z.record(z.string(), z.unknown()),
366
+ headers: z.record(z.string(), z.string()).optional(),
367
+ }))
368
+ .min(1),
369
+ },
370
+ handler: (args) => {
371
+ const endpoint = `${trimitBase()}/$batch`;
372
+ const body = {
373
+ requests: args.requests.map((r) => ({
374
+ method: r.method,
375
+ url: r.url,
376
+ body: r.body,
377
+ headers: r.headers ?? { "Content-Type": "application/json" },
378
+ })),
379
+ };
380
+ return {
381
+ endpoint,
382
+ method: "POST",
383
+ headers: buildHeaders(),
384
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
385
+ queryParams: {},
386
+ body,
387
+ description: "Batch sales document creates.",
388
+ docsUrl: "https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/webservices/odata-using-batch",
389
+ codeExample: fetchExample(endpoint, "POST", body),
390
+ auth: TRIMIT_AUTH,
391
+ notes: "BC $batch envelope follows the OData v4 JSON batch spec. The whole batch fails together only if framing is invalid; individual requests get their own status. Each sub-URL is relative to the OData service root.",
392
+ };
393
+ },
394
+ },
395
+ ];
@@ -0,0 +1,228 @@
1
+ import { z } from "zod";
2
+ import { HOST, STD_API_PATH, TRIMIT_API_PATH, TRIMIT_AUTH, buildHeaders, buildOdataQuery, fetchExample, stdBase, stdRoot, tenantRoot, } from "../common.js";
3
+ const CATEGORY = "standard";
4
+ export const standardTools = [
5
+ {
6
+ name: "trimit_std_get_companies",
7
+ description: "List Business Central companies in the tenant (Standard MS API). Returns each company id GUID — required for every TRIMIT call.",
8
+ category: CATEGORY,
9
+ zodShape: {
10
+ filter: z.string().optional().describe("OData $filter expression"),
11
+ select: z.array(z.string()).optional().describe("Fields to return"),
12
+ },
13
+ handler: (args) => {
14
+ const { qs, params } = buildOdataQuery({ filter: args.filter, select: args.select });
15
+ const endpoint = `${stdRoot()}/companies${qs}`;
16
+ return {
17
+ endpoint,
18
+ method: "GET",
19
+ headers: buildHeaders(),
20
+ pathParams: { tenant: "{tenant}", environment: "{environment}" },
21
+ queryParams: params,
22
+ body: null,
23
+ description: "List BC companies via the standard MS BC API. Use a company id with every other TRIMIT call.",
24
+ docsUrl: "https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/api/dynamics_company",
25
+ codeExample: fetchExample(endpoint, "GET", null),
26
+ auth: TRIMIT_AUTH,
27
+ notes: "Standard BC API base path is /api/v2.0; this does NOT live under the TRIMIT integration path. Response is OData v4 JSON: { '@odata.context': ..., value: [{ id, name, displayName, ... }] }.",
28
+ };
29
+ },
30
+ },
31
+ {
32
+ name: "trimit_std_get_companies_odata",
33
+ description: "List companies via the BC OData v4 endpoint (parallel to the Standard API). Returns the same data via OData.",
34
+ category: CATEGORY,
35
+ zodShape: {
36
+ filter: z.string().optional(),
37
+ select: z.array(z.string()).optional(),
38
+ top: z.number().optional(),
39
+ skip: z.number().optional(),
40
+ },
41
+ handler: (args) => {
42
+ const { qs, params } = buildOdataQuery(args);
43
+ const endpoint = `${tenantRoot()}/ODataV4/Company${qs}`;
44
+ return {
45
+ endpoint,
46
+ method: "GET",
47
+ headers: buildHeaders(),
48
+ pathParams: { tenant: "{tenant}", environment: "{environment}" },
49
+ queryParams: params,
50
+ body: null,
51
+ description: "OData v4 view of companies.",
52
+ docsUrl: "https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/webservices/odata-web-services",
53
+ codeExample: fetchExample(endpoint, "GET", null),
54
+ auth: TRIMIT_AUTH,
55
+ notes: "OData endpoint is /ODataV4 (not /api/...). Useful for legacy OData clients.",
56
+ };
57
+ },
58
+ },
59
+ {
60
+ name: "trimit_std_get_items",
61
+ description: "List items via the Standard MS BC API (BC built-in Item table — not the TRIMIT-enriched item resource).",
62
+ category: CATEGORY,
63
+ zodShape: {
64
+ filter: z.string().optional(),
65
+ select: z.array(z.string()).optional(),
66
+ expand: z.string().optional(),
67
+ top: z.number().optional(),
68
+ skip: z.number().optional(),
69
+ },
70
+ handler: (args) => {
71
+ const { qs, params } = buildOdataQuery(args);
72
+ const endpoint = `${stdBase()}/items${qs}`;
73
+ return {
74
+ endpoint,
75
+ method: "GET",
76
+ headers: buildHeaders(),
77
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
78
+ queryParams: params,
79
+ body: null,
80
+ description: "Standard BC items list.",
81
+ docsUrl: "https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/api/dynamics_item",
82
+ codeExample: fetchExample(endpoint, "GET", null),
83
+ auth: TRIMIT_AUTH,
84
+ notes: "For TRIMIT-extended items (variants, master link, extended descriptions) use trimit_products_get_items instead.",
85
+ };
86
+ },
87
+ },
88
+ {
89
+ name: "trimit_std_get_customers",
90
+ description: "List customers via the Standard MS BC API with default dimensions expanded.",
91
+ category: CATEGORY,
92
+ zodShape: {
93
+ filter: z.string().optional(),
94
+ select: z.array(z.string()).optional(),
95
+ expand: z
96
+ .string()
97
+ .optional()
98
+ .describe("OData $expand; defaults to 'defaultDimensions' if omitted"),
99
+ top: z.number().optional(),
100
+ skip: z.number().optional(),
101
+ },
102
+ handler: (args) => {
103
+ const expand = args.expand ?? "defaultDimensions";
104
+ const { qs, params } = buildOdataQuery({ ...args, expand });
105
+ const endpoint = `${stdBase()}/customers${qs}`;
106
+ return {
107
+ endpoint,
108
+ method: "GET",
109
+ headers: buildHeaders(),
110
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}" },
111
+ queryParams: params,
112
+ body: null,
113
+ description: "List BC customers with default dimensions.",
114
+ docsUrl: "https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/api/dynamics_customer",
115
+ codeExample: fetchExample(endpoint, "GET", null),
116
+ auth: TRIMIT_AUTH,
117
+ notes: "Use trimit_customers_list for the TRIMIT-enriched customer resource (picture, priceGroupParameters, contactsInformation, configuredFields, etc.).",
118
+ };
119
+ },
120
+ },
121
+ {
122
+ name: "trimit_std_patch_customer",
123
+ description: "Patch a customer's default dimensions via the Standard MS BC API. Requires If-Match header for OData concurrency control.",
124
+ category: CATEGORY,
125
+ zodShape: {
126
+ customerId: z.string().describe("Customer systemId GUID"),
127
+ ifMatch: z.string().default("*").describe("ETag for OData concurrency. '*' = unconditional."),
128
+ defaultDimensions: z
129
+ .array(z.object({
130
+ parentType: z.literal("Customer"),
131
+ parentId: z.string(),
132
+ dimensionCode: z.string(),
133
+ dimensionValueCode: z.string(),
134
+ }))
135
+ .describe("Default dimension assignments"),
136
+ },
137
+ handler: (args) => {
138
+ const endpoint = `${stdBase()}/customers(${args.customerId})?$expand=defaultDimensions`;
139
+ const body = { defaultDimensions: args.defaultDimensions };
140
+ const headers = buildHeaders({ "If-Match": args.ifMatch ?? "*" });
141
+ return {
142
+ endpoint,
143
+ method: "PATCH",
144
+ headers,
145
+ pathParams: { tenant: "{tenant}", environment: "{environment}", companyId: "{companyId}", customerId: args.customerId },
146
+ queryParams: { $expand: "defaultDimensions" },
147
+ body,
148
+ description: "Patch customer default dimensions.",
149
+ docsUrl: "https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/api/dynamics_customer_update",
150
+ codeExample: fetchExample(endpoint, "PATCH", body, { "If-Match": args.ifMatch ?? "*" }),
151
+ auth: TRIMIT_AUTH,
152
+ notes: "BC OData requires If-Match for PATCH. Use '*' for unconditional update or the resource @odata.etag value for optimistic concurrency.",
153
+ };
154
+ },
155
+ },
156
+ {
157
+ name: "trimit_std_get_metadata",
158
+ description: "Get the CSDL/EDMX metadata document for the Standard MS BC API ($metadata).",
159
+ category: CATEGORY,
160
+ zodShape: {},
161
+ handler: () => {
162
+ const endpoint = `${HOST}/{tenant}/{environment}/${STD_API_PATH}/$metadata`;
163
+ return {
164
+ endpoint,
165
+ method: "GET",
166
+ headers: buildHeaders({ Accept: "application/xml" }),
167
+ pathParams: { tenant: "{tenant}", environment: "{environment}" },
168
+ queryParams: {},
169
+ body: null,
170
+ description: "Standard BC API $metadata (XML).",
171
+ docsUrl: "https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/",
172
+ codeExample: fetchExample(endpoint, "GET", null, { Accept: "application/xml" }),
173
+ auth: TRIMIT_AUTH,
174
+ notes: "Use for client codegen (e.g. odata2ts, openapi-from-edmx). Returns XML, not JSON.",
175
+ };
176
+ },
177
+ },
178
+ {
179
+ name: "trimit_std_get_metadata_odata",
180
+ description: "Get the CSDL/EDMX metadata document for the BC OData v4 endpoint.",
181
+ category: CATEGORY,
182
+ zodShape: {},
183
+ handler: () => {
184
+ const endpoint = `${tenantRoot()}/ODataV4/$metadata`;
185
+ return {
186
+ endpoint,
187
+ method: "GET",
188
+ headers: buildHeaders({ Accept: "application/xml" }),
189
+ pathParams: { tenant: "{tenant}", environment: "{environment}" },
190
+ queryParams: {},
191
+ body: null,
192
+ description: "Metadata for the BC ODataV4 endpoint (different from the /api/v2.0 metadata).",
193
+ docsUrl: "https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/webservices/odata-web-services",
194
+ codeExample: fetchExample(endpoint, "GET", null, { Accept: "application/xml" }),
195
+ auth: TRIMIT_AUTH,
196
+ notes: null,
197
+ };
198
+ },
199
+ },
200
+ {
201
+ name: "trimit_std_get_entity_definitions",
202
+ description: "List entity definitions in the Standard MS BC API.",
203
+ category: CATEGORY,
204
+ zodShape: {},
205
+ handler: () => {
206
+ const endpoint = `${HOST}/{tenant}/{environment}/${STD_API_PATH}/entityDefinitions`;
207
+ return {
208
+ endpoint,
209
+ method: "GET",
210
+ headers: buildHeaders(),
211
+ pathParams: { tenant: "{tenant}", environment: "{environment}" },
212
+ queryParams: {},
213
+ body: null,
214
+ description: "Discover entity definitions (BC entity schema introspection).",
215
+ docsUrl: "https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/api-reference/v2.0/",
216
+ codeExample: fetchExample(endpoint, "GET", null),
217
+ auth: TRIMIT_AUTH,
218
+ notes: null,
219
+ };
220
+ },
221
+ },
222
+ ];
223
+ // Re-export base paths for help tools
224
+ export const _STANDARD_BASES = {
225
+ std: STD_API_PATH,
226
+ trimit: TRIMIT_API_PATH,
227
+ host: HOST,
228
+ };