node-autotask 0.39.3 → 0.40.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.
Files changed (44) hide show
  1. package/ARCHITECTURE.md +6 -2
  2. package/CHANGELOG.md +32 -0
  3. package/MCP_TOOL_CATALOG.json +3 -3
  4. package/MCP_TOOL_MANIFEST.md +5 -5
  5. package/N8N_MCP_MANIFEST.json +2 -2
  6. package/N8N_MCP_MANIFEST.md +3 -3
  7. package/README.md +28 -36
  8. package/capabilities.json +1315 -1320
  9. package/dist/base-CVx2VMSj.d.cts +103 -0
  10. package/dist/base-CVx2VMSj.d.ts +103 -0
  11. package/dist/capabilities.cjs +2 -2
  12. package/dist/capabilities.js +1 -1
  13. package/dist/{chunk-MBI24PEF.cjs → chunk-E5W24BZB.cjs} +143 -143
  14. package/dist/{chunk-F4YDKUGY.cjs → chunk-EUGSF3U4.cjs} +287 -292
  15. package/dist/{chunk-VR6RYXTD.cjs → chunk-EVDVESQM.cjs} +1314 -1319
  16. package/dist/{chunk-BPRDYT5L.cjs → chunk-FEYVLIHD.cjs} +738 -1699
  17. package/dist/{chunk-5JAS6CAN.js → chunk-MTFRSD7P.js} +2 -2
  18. package/dist/{chunk-K6RZDRWX.js → chunk-O32L6GS5.js} +4 -9
  19. package/dist/{chunk-GSMFRPYB.js → chunk-VDVBXXJW.js} +570 -1531
  20. package/dist/{chunk-GDSCYY2G.js → chunk-Y6YJYKBZ.js} +1314 -1319
  21. package/dist/{index-DkZVRMeW.d.ts → index-BKp8_P2L.d.cts} +125 -806
  22. package/dist/{index-DkZVRMeW.d.cts → index-BKp8_P2L.d.ts} +125 -806
  23. package/dist/index.cjs +22 -50
  24. package/dist/index.d.cts +103 -102
  25. package/dist/index.d.ts +103 -102
  26. package/dist/index.js +18 -46
  27. package/dist/operations/index.cjs +178 -178
  28. package/dist/operations/index.d.cts +5 -4
  29. package/dist/operations/index.d.ts +5 -4
  30. package/dist/operations/index.js +3 -3
  31. package/dist/pool.cjs +17 -60
  32. package/dist/pool.d.cts +5 -13
  33. package/dist/pool.d.ts +5 -13
  34. package/dist/pool.js +13 -56
  35. package/dist/resources/index.cjs +4 -4
  36. package/dist/resources/index.d.cts +1 -1
  37. package/dist/resources/index.d.ts +1 -1
  38. package/dist/resources/index.js +3 -3
  39. package/dist/types/index.d.cts +1 -1
  40. package/dist/types/index.d.ts +1 -1
  41. package/docs/API.md +131 -320
  42. package/docs/N8N-ADAPTER.md +7 -7
  43. package/examples/mcp-server-http.ts +18 -18
  44. package/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Status:** v0.1.0 (the 121-entity MVP set). Generated tables are marked
4
4
  `GENERATED` and regenerated by `npm run docs:render` — two JSONs in
5
- (`.run/spec/n8n-mapping.json` × `capabilities.json`, plus the plan × the
5
+ (`scripts/n8n-mapping.json` × `capabilities.json`, plus the plan × the
6
6
  vendored `api-docs.json` for the gap table), markdown table out. Do not
7
7
  hand-type the generated regions.
8
8
 
@@ -30,7 +30,7 @@ Autotask semantic is an SDK call.
30
30
  ## 2. Operation mapping — node resource → SDK operations
31
31
 
32
32
  <!-- BEGIN GENERATED:n8n-op-mapping -->
33
- Two sources in, markdown table out: the node repo (the ops column is re-derived from the `name: 'operation'` property options of each `resources/<dir>` dir — style A const or inline options; values of any other dropdown property, e.g. the timeEntries dateRange presets, are filter options and never ops) × `capabilities.json` (SDK operations per resource key, generated registry). `.run/spec/n8n-mapping.json` supplies the row set (node dir → apiEntity), the parent-scoped flag and the curated special ops. Standard node ops map to the SDK primitive of the same name (`getMany` → `queryAll`, the unbounded raw walk — the bounded MCP read is the `query` op, routed by `invoke` to `queryBounded`). A node special op maps to the SDK operation of the same name on the same resource when the registry has it; `—` = the node op has no SDK operation of its own (handled in the adjacent sections).
33
+ Two sources in, markdown table out: the node repo (the ops column is re-derived from the `name: 'operation'` property options of each `resources/<dir>` dir — style A const or inline options; values of any other dropdown property, e.g. the timeEntries dateRange presets, are filter options and never ops) × `capabilities.json` (SDK operations per resource key, generated registry). `scripts/n8n-mapping.json` supplies the row set (node dir → apiEntity), the parent-scoped flag and the curated special ops. Standard node ops map to the SDK primitive of the same name (`getMany` → `queryAll`, the unbounded raw walk — the bounded MCP read is the `query` op, routed by `invoke` to `queryBounded`). A node special op maps to the SDK operation of the same name on the same resource when the registry has it; `—` = the node op has no SDK operation of its own (handled in the adjacent sections).
34
34
 
35
35
  | node dir | apiEntity | parent-scoped | node ops (opsImplemented) | node special ops | SDK methods (from the registry) |
36
36
  |----------|-----------|---------------|---------------------------|------------------|----------------------------------|
@@ -41,7 +41,7 @@ Two sources in, markdown table out: the node repo (the ops column is re-derived
41
41
  | `checklistLibraries` | ChecklistLibrary | no | create, update, delete, get, getMany, count | — | count, create, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search, update |
42
42
  | `checklistLibraryChecklistItems` | ChecklistLibraryChecklistItem | yes | create, update, delete, get, getMany, count | — | count, create, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, update |
43
43
  | `classificationIcons` | ClassificationIcon | no | get, getMany, count | — | count, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search |
44
- | `companies` | Company | no | create, update, get, getMany, count, searchByIdentity, searchByDomain | searchByDomain → `companies.searchByDomain`; searchByIdentity → `companies.searchByIdentity` | count, create, fullProfile, get, getContext, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search, searchByDomain, searchByIdentity, unbilledWork, update, upsertByName |
44
+ | `companies` | Company | no | create, update, get, getMany, count, searchByIdentity, searchByDomain | searchByDomain → `companies.searchByDomain`; searchByIdentity → `companies.searchByIdentity` | count, create, fullProfile, get, getContext, getEntityInfo, getFieldInfo, getUdfInfo, prepareCrmWrite, query, resolve, search, searchByDomain, searchByIdentity, unbilledWork, update, upsertByName |
45
45
  | `companyAlerts` | CompanyAlert | yes | create, update, delete, get, getMany, count | — | count, create, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, update |
46
46
  | `companyLocations` | CompanyLocation | yes | create, get, getMany, update, delete, count | — | count, create, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search, update |
47
47
  | `companyNotes` | CompanyNote | yes | create, update, get, getMany, count | — | count, create, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search, update |
@@ -82,7 +82,7 @@ Two sources in, markdown table out: the node repo (the ops column is re-derived
82
82
  | `contractServiceUnits` | ContractServiceUnit | yes | get, getMany, count | — | count, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve |
83
83
  | `contractServices` | ContractService | yes | create, update, get, getMany, count, createIfNotExists | createIfNotExists → `contract_services.createIfNotExists` | count, create, createIfNotExists, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, update |
84
84
  | `contractTicketPurchases` | ContractTicketPurchases | yes | create, update, get, getMany, count | — | count, create, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, update |
85
- | `contracts` | Contract | no | create, update, get, getMany, count, createIfNotExists | createIfNotExists → `contracts.createIfNotExists` | count, create, createIfNotExists, get, getEntityInfo, getFieldInfo, getUdfInfo, profitability, query, resolve, search, update |
85
+ | `contracts` | Contract | no | create, update, get, getMany, count, createIfNotExists | createIfNotExists → `contracts.createIfNotExists` | count, create, createIfNotExists, get, getEntityInfo, getFieldInfo, getUdfInfo, lineOfBusiness, profitability, query, resolve, search, update |
86
86
  | `countries` | Country | no | get, getMany, update, count | — | count, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search, update |
87
87
  | `departments` | Department | no | count, create, get, getMany, update | — | count, create, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search, update |
88
88
  | `domainRegistrar` | DomainRegistrar | no | create, update, get, getMany, count | — | count, create, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search, update |
@@ -141,7 +141,7 @@ Two sources in, markdown table out: the node repo (the ops column is re-derived
141
141
  | `ticketCategories` | TicketCategory | no | get, getMany, update, count | — | count, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search, update |
142
142
  | `ticketCategoryFieldDefaults` | TicketCategoryFieldDefault | yes | get, getMany, count | — | count, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve |
143
143
  | `ticketChangeRequestApprovals` | TicketChangeRequestApproval | yes | create, delete, get, getMany, count | — | count, create, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve |
144
- | `ticketCharges` | TicketCharge | yes | create, createIfNotExists, update, delete, get, getMany, count | createIfNotExists → `ticket_charges.createIfNotExists` | count, create, createIfNotExists, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search, update |
144
+ | `ticketCharges` | TicketCharge | yes | create, createIfNotExists, update, delete, get, getMany, count | createIfNotExists → `ticket_charges.createIfNotExists` | count, create, createIfNotExists, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, moveToTicket, query, resolve, search, update |
145
145
  | `ticketChecklistItems` | TicketChecklistItem | yes | create, update, delete, get, getMany, count | — | count, create, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, update |
146
146
  | `ticketChecklistLibraries` | TicketChecklistLibrary | yes | create | — | attachToDocument, create, getEntityInfo, getFieldInfo, getUdfInfo, resolve |
147
147
  | `ticketHistories` | TicketHistory | no | get, getMany, count | — | count, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve |
@@ -150,8 +150,8 @@ Two sources in, markdown table out: the node repo (the ops column is re-derived
150
150
  | `ticketNotes` | TicketNote | yes | create, update, get, getMany, count | — | count, create, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, update |
151
151
  | `ticketSecondaryResources` | TicketSecondaryResource | yes | create, delete, get, getMany, count | — | count, create, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve |
152
152
  | `ticketWebhooks` | TicketWebhook | yes | get, getMany, delete | — | count, create, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, search, update |
153
- | `tickets` | Ticket | no | create, get, getMany, slaHealthCheck, summary, timeline, update, count | slaHealthCheck → `tickets.slaHealthCheck`; summary → `tickets.summary`; timeline → `tickets.timeline` | changeStatus, count, create, findByTicketNumber, fullContext, get, getContext, getEntityInfo, getFieldInfo, getUdfInfo, openWithNoteAndAssign, query, resolve, search, slaHealthCheck, summary, timeline, update |
154
- | `timeEntries` | TimeEntry | no | create, update, get, getMany, getPosted, getUnposted, delete, count, createIfNotExists | createIfNotExists → `time_entries.createIfNotExists`; customRange → `time_entries.customRange`; getPosted → `time_entries.getPosted`; getUnposted → `time_entries.getUnposted`; last14 → `time_entries.last14`; last24h → `time_entries.last24h`; last30 → `time_entries.last30`; last7 → `time_entries.last7`; last90 → `time_entries.last90`; lastFullFortnight → `time_entries.lastFullFortnight`; lastFullMonth → `time_entries.lastFullMonth`; lastFullQuarter → `time_entries.lastFullQuarter`; lastFullWeek → `time_entries.lastFullWeek`; today → `time_entries.today`; yesterday → `time_entries.yesterday` | count, create, createForResource, createIfNotExists, customRange, dateWorkedWindow, delete, get, getEntityInfo, getFieldInfo, getPosted, getUdfInfo, getUnposted, last14, last24h, last30, last7, last90, lastFullFortnight, lastFullMonth, lastFullQuarter, lastFullWeek, postedWithin, query, resolve, today, update, yesterday |
153
+ | `tickets` | Ticket | no | create, get, getMany, slaHealthCheck, summary, timeline, update, count | slaHealthCheck → `tickets.slaHealthCheck`; summary → `tickets.summary`; timeline → `tickets.timeline` | changeStatus, count, create, findByTicketNumber, forResource, fullContext, get, getContext, getEntityInfo, getFieldInfo, getUdfInfo, lineOfBusiness, openWithNoteAndAssign, query, resolve, search, slaHealthCheck, statusSemantics, summary, timeline, update |
154
+ | `timeEntries` | TimeEntry | no | create, update, get, getMany, getPosted, getUnposted, delete, count, createIfNotExists | createIfNotExists → `time_entries.createIfNotExists`; customRange → `time_entries.customRange`; getPosted → `time_entries.getPosted`; getUnposted → `time_entries.getUnposted`; last14 → `time_entries.last14`; last24h → `time_entries.last24h`; last30 → `time_entries.last30`; last7 → `time_entries.last7`; last90 → `time_entries.last90`; lastFullFortnight → `time_entries.lastFullFortnight`; lastFullMonth → `time_entries.lastFullMonth`; lastFullQuarter → `time_entries.lastFullQuarter`; lastFullWeek → `time_entries.lastFullWeek`; today → `time_entries.today`; yesterday → `time_entries.yesterday` | approve, count, create, createForResource, createIfNotExists, customRange, dateWorkedWindow, delete, get, getEntityInfo, getFieldInfo, getPosted, getUdfInfo, getUnposted, last14, last24h, last30, last7, last90, lastFullFortnight, lastFullMonth, lastFullQuarter, lastFullWeek, moveToTicket, postedWithin, query, recentTickets, resolve, today, update, yesterday |
155
155
  | `timeEntryAttachments` | TimeEntryAttachment | yes | create, getMany, count, download, delete | download → `time_entry_attachments.download` | count, create, delete, download, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve |
156
156
  | `timeOffRequests` | TimeOffRequest | no | get, getMany, count, approve, reject | approve → `time_off_requests.approve`; reject → `time_off_requests.reject` | approve, count, get, getEntityInfo, getFieldInfo, getUdfInfo, query, reject, resolve |
157
157
  | `(none — NOT IMPLEMENTED in node)` | InventoryProduct (StockedItemsAdd/Remove/Transfer pseudo-entities) | yes | | stockedItemsAdd → `inventory_products.stockedItemsAdd`; stockedItemsRemove → `inventory_products.stockedItemsRemove`; stockedItemsTransfer → `inventory_products.stockedItemsTransfer` | count, create, delete, get, getEntityInfo, getFieldInfo, getUdfInfo, query, resolve, stockedItemsAdd, stockedItemsRemove, stockedItemsTransfer, update |
@@ -82,7 +82,7 @@ import { pipeline } from 'node:stream/promises';
82
82
  import type { ReadableStream as NodeReadableStream } from 'node:stream/web';
83
83
  import { createMcpHandler, McpServer } from '@modelcontextprotocol/server';
84
84
  import type { McpServerFactory } from '@modelcontextprotocol/server';
85
- import { AutotaskClient, type ZoneName } from 'node-autotask';
85
+ import { AutotaskClient, type AutotaskConfig, type ZoneName } from 'node-autotask';
86
86
  import { createClientPool, type ClientPool } from 'node-autotask/pool';
87
87
  import { MCP_SERVER_OPTIONS, registerCoreTools, SERVER_INFO, effectForOperation } from './mcp-register.js';
88
88
  import { createStderrAuditSink } from './mcp-audit.js';
@@ -142,21 +142,21 @@ const client = GATEWAY || DRY
142
142
  * is shared between its requests, never across tenants), the user-visible
143
143
  * warning sink, and the shared audit sink.
144
144
  */
145
- const pool: ClientPool | undefined = GATEWAY
146
- ? createClientPool({
147
- factory: (config) =>
148
- new AutotaskClient({
149
- // Issue #4: the SAME env overrides apply to every pooled client —
150
- // spread first, so the per-request tenant config and the pooled read
151
- // cache (explicit) always win over the env (explicit > env > profile).
152
- ...withEnvOverrides(),
153
- ...config,
154
- cache: { ttlMs: GATEWAY_CACHE_TTL_MS },
155
- logger: { warn: (m: string) => console.error(m) },
156
- onAudit: createStderrAuditSink({ resolveEffect: effectForOperation }),
157
- }),
158
- })
159
- : undefined;
145
+ const pool: ClientPool | undefined = GATEWAY ? createClientPool() : undefined;
146
+
147
+ /** The pooled client's config: the tenant's credentials over the shared gateway settings. */
148
+ function pooledConfig(creds: Parameters<typeof gatewayConfig>[0]): AutotaskConfig {
149
+ return {
150
+ // Issue #4: the SAME env overrides apply to every pooled client — spread
151
+ // first, so the per-request tenant config and the pooled read cache
152
+ // (explicit) always win over the env (explicit > env > profile).
153
+ ...withEnvOverrides(),
154
+ ...gatewayConfig(creds),
155
+ cache: { ttlMs: GATEWAY_CACHE_TTL_MS },
156
+ logger: { warn: (m: string) => console.error(m) },
157
+ onAudit: createStderrAuditSink({ resolveEffect: effectForOperation }),
158
+ };
159
+ }
160
160
 
161
161
  /** The pooled client for one gateway request (null = DRY surface). */
162
162
  function gatewayClientFor(request: Request | undefined): { client: AutotaskClient | null; dry: boolean } {
@@ -165,7 +165,7 @@ function gatewayClientFor(request: Request | undefined): { client: AutotaskClien
165
165
  // re-parse (the same pure function) only selects the pooled client.
166
166
  const parsed = extractGatewayCredentials(request.headers);
167
167
  if ('status' in parsed) return { client: null, dry: true };
168
- return { client: pool.get(gatewayConfig(parsed)), dry: false };
168
+ return { client: pool.get(pooledConfig(parsed)), dry: false };
169
169
  }
170
170
 
171
171
  /** One `McpServer` per HTTP request; the tool surface is global (same as stdio). */
@@ -208,7 +208,7 @@ async function fetchHandler(request: Request): Promise<Response> {
208
208
  headers: { 'content-type': 'text/plain; charset=utf-8' },
209
209
  });
210
210
  }
211
- const gwClient = pool.get(gatewayConfig(parsed));
211
+ const gwClient = pool.get(pooledConfig(parsed));
212
212
  // Bounded warm-up: the tenant's probe is awaited for AT MOST
213
213
  // GATEWAY_WARMUP_BUDGET_MS (fixed), then the request proceeds without
214
214
  // it — a cold cache on a large tenant cannot push the call past the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-autotask",
3
- "version": "0.39.3",
3
+ "version": "0.40.0",
4
4
  "description": "Fully-typed TypeScript SDK for the Autotask (Datto) PSA REST API. Zero runtime dependencies. Built for building MCP servers and integrations.",
5
5
  "type": "module",
6
6
  "sideEffects": false,