node-autotask 0.13.0 → 0.15.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 +43 -7
  2. package/MCP_TOOL_CATALOG.json +1 -1
  3. package/MCP_TOOL_MANIFEST.md +2 -2
  4. package/N8N_MCP_MANIFEST.json +2 -2
  5. package/N8N_MCP_MANIFEST.md +3 -3
  6. package/README.md +9 -8
  7. package/capabilities.json +2132 -2740
  8. package/dist/capabilities.cjs +2 -2
  9. package/dist/capabilities.d.cts +9 -4
  10. package/dist/capabilities.d.ts +9 -4
  11. package/dist/capabilities.js +1 -1
  12. package/dist/{chunk-Y6O5VVQ4.js → chunk-AXVYQPTB.js} +136 -126
  13. package/dist/{chunk-IKJLIDK3.js → chunk-CSA6QSV6.js} +37 -25
  14. package/dist/{chunk-CXXNONRP.cjs → chunk-EWMG3HHU.cjs} +2131 -2739
  15. package/dist/{chunk-OZD5FQM4.js → chunk-JIDR3NPV.js} +2131 -2739
  16. package/dist/{chunk-DDZXN6LN.js → chunk-LH5BXQ7A.js} +2 -2
  17. package/dist/{chunk-CJA6L6NR.cjs → chunk-V7QPZA7L.cjs} +135 -135
  18. package/dist/{chunk-MTDCLVQQ.cjs → chunk-VLIUHEPE.cjs} +558 -548
  19. package/dist/{chunk-KB2ANVVR.cjs → chunk-Z7YT72SS.cjs} +42 -30
  20. package/dist/{index-DbMKWnEX.d.cts → index-BXIwhAN4.d.cts} +67 -13
  21. package/dist/{index-TuqNq3Ro.d.ts → index-M0qJIiRt.d.ts} +67 -13
  22. package/dist/index.cjs +61 -19
  23. package/dist/index.d.cts +83 -3
  24. package/dist/index.d.ts +83 -3
  25. package/dist/index.js +46 -4
  26. package/dist/operations/index.cjs +110 -107
  27. package/dist/operations/index.d.cts +9 -2
  28. package/dist/operations/index.d.ts +9 -2
  29. package/dist/operations/index.js +9 -6
  30. package/dist/pool.cjs +8 -8
  31. package/dist/pool.d.cts +1 -1
  32. package/dist/pool.d.ts +1 -1
  33. package/dist/pool.js +4 -4
  34. package/dist/resources/index.cjs +4 -4
  35. package/dist/resources/index.d.cts +1 -1
  36. package/dist/resources/index.d.ts +1 -1
  37. package/dist/resources/index.js +3 -3
  38. package/dist/untrusted.cjs +3 -1
  39. package/dist/untrusted.d.cts +9 -4
  40. package/dist/untrusted.d.ts +9 -4
  41. package/dist/untrusted.js +3 -1
  42. package/docs/API.md +112 -22
  43. package/docs/LIVE-TESTING.md +1 -1
  44. package/package.json +2 -2
package/ARCHITECTURE.md CHANGED
@@ -304,7 +304,7 @@ export interface AutotaskConfig {
304
304
  pageSize?: number;
305
305
  /** HTTP timeout in ms (whole-call budget). Default 30_000. */
306
306
  timeoutMs?: number;
307
- /** Retry budget for idempotent requests on 429/5xx. Default 3 (0 disables). */
307
+ /** The client's retry budget (default 3; 0 disables auto-retry). Three legs, two rules (issues #105/#109): the 429 leg retries on the pre-#105 retriable set (GET/PUT/DELETE + idempotent-flagged calls); the 5xx/network leg and the non-JSON-2xx (#24) leg retry GET + idempotent-flagged calls only. A per-call override rides `InvokeOptions.maxRetries` / `MutationOpts.maxRetries`. */
308
308
  maxRetries?: number;
309
309
  /** Optional request log sink. Default: no-op. */
310
310
  logger?: Logger;
@@ -510,10 +510,36 @@ export interface RequestOptions {
510
510
  - `2xx` → parse JSON (if body present); empty body → resolve `undefined`.
511
511
  - non-2xx → throw via `errorFromStatus(status, body, url, operation)` (§10).
512
512
  - network/AbortError → `AutotaskNetworkError` / `AutotaskTimeoutError`.
513
- 6. **Retries**: on `429`/`5xx`, retry up to `maxRetries` with exponential backoff + jitter
514
- (base 200 ms, cap 30 s, honour `Retry-After`). Only **idempotent** methods are retried:
515
- `GET`, `PUT`, `DELETE`; `POST`/`PATCH` are NOT (the parent-scoped `GET .../Approve` is a
516
- `GET` and therefore retried like any GET; the `POST .../Reject` is not).
513
+ 6. **Retries** (issues #105/#109 — three legs, two rules, retry up to `maxRetries` with
514
+ exponential backoff + jitter, base 200 ms, cap 30 s, honouring `Retry-After`):
515
+ - **429 leg — UNCHANGED by #105.** A `429` is a **pre-processing refusal** (the
516
+ vendor rate limiter rejected the call before executing it); it is retried on the
517
+ pre-#105 retriable set — `GET`/`PUT`/`DELETE` + explicitly idempotent-flagged
518
+ calls (`IDEMPOTENT_METHODS`, kept exported for the public-surface baseline). A
519
+ 429 on an unflagged `POST`/`PATCH` surfaces at once, exactly as before #105.
520
+ - **5xx / network leg — FLIPPED by #105.** Only `GET` and explicitly
521
+ idempotent-flagged calls retry; a mutation (`POST`/`PUT`/`PATCH`/`DELETE`) makes
522
+ exactly one send — the write-outcome is unknown once the body was written (the
523
+ #104 classifier: never blindly resend). An explicit `idempotent: true` on a
524
+ transport-level `RequestOptions` (not exposed on the mutation helpers) opts
525
+ back into the pre-#105 auto-retry on both legs. The parent-scoped
526
+ `GET .../Approve` is a `GET` and therefore retried like any GET; the
527
+ `POST .../Reject` is never retried on either leg — it is an unflagged `POST`, so a
528
+ `429` surfaces at once as well (`time_off_requests.reject` lists no retryable
529
+ statuses in the registry).
530
+ - **`#24` leg (non-JSON `2xx`, an edge maintenance page) — follows the write
531
+ rule (issue #109).** A `GET` / explicitly idempotent-flagged call retries; a
532
+ mutation makes ONE send and throws `UNEXPECTED_CONTENT_TYPE` with the residue
533
+ `suggestedAction`. The transport no longer assumes the edge served the page —
534
+ that premise is unprovable from the response, and the #104 classifier pins this
535
+ code as `unknown` for a write (never blindly resend). `idempotent: true` /
536
+ `maxRetries` remain the explicit opt-ins.
537
+ - **Per-call budget override:** `InvokeOptions.maxRetries` / `MutationOpts.maxRetries`
538
+ threads the transport's per-request override through a mutation (and its
539
+ auxiliary reads, e.g. a partial-update merge read); `0` = exactly one send per
540
+ request the call performs (default: the client's configured `maxRetries`). The
541
+ optional stale-object guard's verification read (only with a
542
+ `lastModifiedDate` token) keeps the client budget.
517
543
  7. **Concurrency guard**: an in-process semaphore bounds concurrent in-flight requests to
518
544
  `maxConcurrent` (default 10 — the API's limit; the node self-limits to 3 per endpoint
519
545
  under post-2023.1 guidance — the SDK default is the API limit and is configurable).
@@ -673,8 +699,12 @@ export function isAutotaskError(err: unknown): err is AutotaskError;
673
699
  GETs) return 200 with `{ item: null }`. The SDK converts that to `NotFoundError`
674
700
  (`category: not_found`, `httpStatus: 200`, `resourceIds: [id]`). This is the vendor's
675
701
  not-found signal and must not be surfaced as a successful empty record.
676
- - **Agent decision mapping** (fields, not string parsing): retry on `retryable: true`;
677
- change input on `category: 'validation'` (`suggestedAction` names the field); ask the
702
+ - **Agent decision mapping** (fields, not string parsing): retry on `retryable: true`
703
+ **for reads**; for a WRITE decide with `classifyWriteOutcome` (docs/API.md §2.2) — its
704
+ `unknown` state is never retryable and is resolved with a read-back (issues #105/#107:
705
+ the transport's `retryable` on a plain 5xx `ServerError` serves the read path, not the
706
+ writer); change input on `category: 'validation'` (`suggestedAction` names the field);
707
+ ask the
678
708
  user on `category: 'resolution'` with candidates in `resourceIds`; resolve a parent
679
709
  first on `category: 'validation'` with the missing parent in `resourceIds`; request
680
710
  approval on the registry's `flags.requiresApproval` / `effect: 'destructive'`; stop on
@@ -1118,6 +1148,12 @@ Floor (plan rows pre-declare it; G6 implements):
1118
1148
  candidates per resource).
1119
1149
 
1120
1150
  ### 19.3 Mutation safety
1151
+ - **Write outcome (issues #105/#107)**: a mutation makes EXACTLY ONE send on a
1152
+ 5xx/network error (two-leg retry policy, §8 "Behaviour" item 6 — a 429 is still retried on
1153
+ the pre-#105 set). A writer classifying a write error uses
1154
+ `classifyWriteOutcome` (docs/API.md §2.2): `failed` = definitely not
1155
+ applied; `unknown` = outcome unprovable — resolve with a read-back, NEVER
1156
+ a blind resend (the vendor offers no idempotency keys).
1121
1157
  - **Classification** (registry `effect` + `flags`, never one combined enum):
1122
1158
  `effect: read | write | destructive`; `flags: sensitive | idempotent | requiresApproval`.
1123
1159
  Plan defaults: `create` = write (non-idempotent), `update` = write + idempotent (PUT
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "generatedBy": "scripts/build-tool-catalog.mjs",
3
- "generatedAt": "2026-10-16T10:52:08.623Z",
3
+ "generatedAt": "2026-10-17T13:48:44.078Z",
4
4
  "planHash": "bd88648453f58ede018062ce66acefacb672fecaa450af89f421057809971e14",
5
5
  "metaTools": [
6
6
  {
@@ -1,6 +1,6 @@
1
1
  # MCP Tool Manifest — node-autotask
2
2
 
3
- <!-- GENERATED by scripts/project-mcp-tools.mjs at 2026-10-16T10:55:23.945Z — do not hand-edit. -->
3
+ <!-- GENERATED by scripts/project-mcp-tools.mjs at 2026-10-17T13:48:43.787Z — do not hand-edit. -->
4
4
 
5
5
  Mechanical MCP projection of the capability registry (generator-design section 7, ARCHITECTURE section 19.5).
6
6
  Tool names are the plan `mcpTool` columns (entity-first `autotask_<entity>_<verb>`); descriptions are the
@@ -947,7 +947,7 @@ singleton, so no helper tier is expected there; G8, 2026-09-26.)
947
947
 
948
948
  ## Meta
949
949
 
950
- - generatedAt: 2026-10-16T10:55:23.945Z
950
+ - generatedAt: 2026-10-17T13:48:43.787Z
951
951
  - planHash: bd88648453f58ede018062ce66acefacb672fecaa450af89f421057809971e14
952
952
  - registry records: 1213
953
953
  - projected tools: 850
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "node-autotask n8n consumer mcp projection v1",
3
- "generatedAt": "2026-10-16T10:52:08.458Z",
3
+ "generatedAt": "2026-10-17T13:48:43.907Z",
4
4
  "generator": "scripts/project-n8n-mcp.mjs",
5
5
  "inputs": {
6
6
  "capabilities": "capabilities.json (the capability registry — the single source of truth)",
@@ -3667,6 +3667,6 @@
3667
3667
  "approvalGatedWrites": 2,
3668
3668
  "destructiveExcluded": 3
3669
3669
  },
3670
- "capabilitiesHash": "9f78c2f6e0af82ae93710a8e024d3aefa54186b8c3b13178f856ac1120546244",
3670
+ "capabilitiesHash": "fd92667b96dd9c74d9bbda2a34bf50b9791e2f236a6d2bb50b50a02a1d064882",
3671
3671
  "curationHash": "e19117ce73e38d9aded3be7e7f077a4e185d2e10294622b76c17d67f63cf9e3b"
3672
3672
  }
@@ -1,6 +1,6 @@
1
1
  # N8N MCP Manifest — node-autotask (consumer projection)
2
2
 
3
- <!-- GENERATED by scripts/project-n8n-mcp.mjs at 2026-10-16T10:52:08.458Z — do not hand-edit. -->
3
+ <!-- GENERATED by scripts/project-n8n-mcp.mjs at 2026-10-17T13:48:43.907Z — do not hand-edit. -->
4
4
 
5
5
  The n8n CONSUMER projection of the capability registry: the canonical source for what the
6
6
  n8n-side MCP tools must expose. The SDK is canonical — this is a projection of it, never a
@@ -282,6 +282,6 @@ only via a recorded change to `n8n-mcp.curation.json`.
282
282
 
283
283
  ## Meta
284
284
 
285
- - generatedAt: 2026-10-16T10:52:08.458Z
286
- - capabilitiesHash: 9f78c2f6e0af82ae93710a8e024d3aefa54186b8c3b13178f856ac1120546244
285
+ - generatedAt: 2026-10-17T13:48:43.907Z
286
+ - capabilitiesHash: fd92667b96dd9c74d9bbda2a34bf50b9791e2f236a6d2bb50b50a02a1d064882
287
287
  - curationHash: e19117ce73e38d9aded3be7e7f077a4e185d2e10294622b76c17d67f63cf9e3b
package/README.md CHANGED
@@ -4,7 +4,7 @@ Fully-typed TypeScript SDK for the Autotask (Datto) PSA REST API. Zero runtime
4
4
  dependencies. Built for building MCP servers and integrations.
5
5
 
6
6
  The SDK is also the **deterministic execution layer for agents** (ARCHITECTURE
7
- section 19): a generated capability registry (1177 operations across the 121-entity
7
+ section 19): a generated capability registry (1213 operations across the 121-entity
8
8
  MVP set), a mechanical MCP substrate (registry → manifest → overrides → core tier +
9
9
  catalog meta-tool), and a write governor (dry-run-first, destructive
10
10
  confirmation). The SDK boundary also redacts response field values — the
@@ -397,9 +397,9 @@ The SDK ships the mechanical substrate, not a server (no MCP imports, no zod):
397
397
  gains two server-category codes: `UNEXPECTED_CONTENT_TYPE` (a non-JSON 2xx
398
398
  whose `Content-Type` is not a JSON media type — the vendor maintenance
399
399
  page; `details` carries the content-type + a 256-byte truncated snippet
400
- that the MCP servers wrap as untrusted host text; idempotent methods retry
401
- per policy, a write makes ONE call and says the write may have taken
402
- effect) and `EMPTY_RESPONSE` (a blank 2xx on a call that expects a result
400
+ that the MCP servers wrap as untrusted host text; a `GET` or an explicitly
401
+ `idempotent`-flagged call retries, a write makes ONE call and says the write
402
+ may have taken effect) and `EMPTY_RESPONSE` (a blank 2xx on a call that expects a result
403
403
  — `expectBody`, non-DELETE by default; `DELETE` / 204 keep `undefined` and
404
404
  the delete ops synthesize `{ id }` from the path id on a blank answer). A
405
405
  vendor field-validation 500 matching the documented phrases (`exceeds
@@ -477,7 +477,7 @@ node-parity paths the vendored swagger lacks, all UNVERIFIED-live this run), and
477
477
  | `npm run capabilities:check` | `scripts/check-capabilities.mjs` — the batch gate (missing/incomplete/stale records → exit 1) |
478
478
  | `npm run public-surface` | `scripts/public-surface.mjs` — capture (`--write`) / verify the public API surface baseline; default mode fails on unannounced removals, **and** checks the BUILT surface: every tsup entry has a matching `exports` key (and vice versa), every declared target was emitted and loads (ESM+CJS), and no runtime dependency leaks into `dist` |
479
479
  | `npm run live-smoke` | `scripts/live-smoke.mjs` — read-only live smoke: 5 vendor probes (5-6 wire calls worst case: 4 GET + 1 bounded query that may walk a 2nd, empty page) + a zero-network shape guard; `--dry` prints the plan with no calls (DRY-RUN-ONLY verdict, exit 0); live via `AUTOTASK_*` env — missing/partial credentials hard-fail exit 2 |
480
- | `npm run live-coverage` | `scripts/live-coverage.mjs` — the live coverage sweep over all 1172 registry ops (`--dry`: full call plan, no calls; live: bounded reads for real, mutations `dryRun: true` only — zero mutating requests, every governor-gated row (the 54 delete dry-runs included) routed through invoke with `confirm`; budget <= 1119 plan-honest / <= 1270 wire-honest worst case) |
480
+ | `npm run live-coverage` | `scripts/live-coverage.mjs` — the live coverage sweep over all 1213 registry ops (`--dry`: full call plan, no calls; live: bounded reads for real, mutations `dryRun: true` only — zero mutating requests, every governor-gated row (the 54 delete dry-runs included) routed through invoke with `confirm`; budget <= 1159 plan-honest / <= 1311 wire-honest worst case) |
481
481
  | `npm run mcp:project` | `scripts/project-mcp-tools.mjs` — mechanical MCP projection → `MCP_TOOL_MANIFEST.md` (`:check` verifies) |
482
482
  | `npm run n8n:project` / `n8n:project:check` | `scripts/project-n8n-mcp.mjs` — the n8n consumer projection from the SAME registry → `N8N_MCP_MANIFEST.json`/`.md` (`--check` rebuilds and diffs; the gate is wired into `verify`) |
483
483
  | `npm run catalog:build` / `catalog:check` | `scripts/build-tool-catalog.mjs` — the catalog meta-tool row data → `MCP_TOOL_CATALOG.json` + mechanical CORE profile (`--check` verifies drift) |
@@ -526,9 +526,10 @@ thresholds: 90/90/80/90 (lines/functions/branches/statements). The public API
526
526
  surface baseline (`test/__fixtures__/public-surface.json`) proves "additive only":
527
527
  a captured name that disappears fails `public-surface`. The two live scripts
528
528
  (`live-smoke`, `live-coverage`) close the mocked-vs-live gap where credentials
529
- exist; this release both ran live against the AU03/ww29 tenant (live-smoke 6/6;
530
- full live-coverage sweep of all 1167 ops, 0 error rows) — the live path is
531
- VERIFIED-live for this release.
529
+ exist; the live path has been verified end-to-end against a real tenant
530
+ (live-smoke 6/6; a full live-coverage sweep with 0 error rows). Re-run both
531
+ commands with your credentials to re-verify against your tenant — the mocked
532
+ suites are the release gate, the live scripts are the ground-truth check.
532
533
 
533
534
  ## Roadmap
534
535