node-autotask 0.18.0 → 0.20.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 (39) hide show
  1. package/ARCHITECTURE.md +15 -8
  2. package/CHANGELOG.md +590 -0
  3. package/MCP_TOOL_CATALOG.json +6 -6
  4. package/MCP_TOOL_MANIFEST.md +128 -128
  5. package/N8N_MCP_MANIFEST.json +44 -9
  6. package/N8N_MCP_MANIFEST.md +8 -8
  7. package/README.md +8 -4
  8. package/capabilities.json +1941 -1829
  9. package/capabilities.schema.json +6 -0
  10. package/dist/capabilities.cjs +2 -2
  11. package/dist/capabilities.js +1 -1
  12. package/dist/{chunk-VV2UUW2J.js → chunk-2T77J2ZM.js} +1940 -1828
  13. package/dist/{chunk-5O5EPYBO.cjs → chunk-FLYTKJKV.cjs} +1940 -1828
  14. package/dist/{chunk-VYUZZIEE.js → chunk-GA6UCX3Z.js} +292 -58
  15. package/dist/{chunk-W2WSZOZA.js → chunk-JEJUFXHX.js} +2 -2
  16. package/dist/{chunk-WRWX6WIK.cjs → chunk-L6V66V22.cjs} +360 -126
  17. package/dist/{chunk-G564CHXM.cjs → chunk-RMLGOCGA.cjs} +135 -135
  18. package/dist/{chunk-HXOWEYV3.js → chunk-XSYJB4IW.js} +3 -3
  19. package/dist/{chunk-7I3F3AJJ.cjs → chunk-YH5CPRDN.cjs} +394 -394
  20. package/dist/{index-D7x3MkLK.d.cts → index-AN-NjLi9.d.cts} +126 -21
  21. package/dist/{index-BqPY04ZG.d.ts → index-CvLLsOCP.d.ts} +126 -21
  22. package/dist/index.cjs +24 -22
  23. package/dist/index.d.cts +2 -2
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +6 -4
  26. package/dist/operations/index.cjs +184 -136
  27. package/dist/operations/index.d.cts +2 -2
  28. package/dist/operations/index.d.ts +2 -2
  29. package/dist/operations/index.js +79 -31
  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/docs/API.md +126 -40
  39. package/package.json +2 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "node-autotask n8n consumer mcp projection v1",
3
- "generatedAt": "2026-10-19T05:26:26.775Z",
3
+ "generatedAt": "2026-10-20T01:49:50.705Z",
4
4
  "generator": "scripts/project-n8n-mcp.mjs",
5
5
  "inputs": {
6
6
  "capabilities": "capabilities.json (the capability registry — the single source of truth)",
@@ -21,7 +21,7 @@
21
21
  "entity": "ticket",
22
22
  "apiEntity": "tickets",
23
23
  "effectLine": "effect=read only — zero mutating requests, ungated by design",
24
- "description": "Read ticket records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for tickets: count: Count ticket records matching a JSON filter. | findByTicketNumber: Find a ticket by its ticketNumber. | fullContext: Get the full context of one ticket in a single call: the ticket, its company, contact, assigned resource and project, plus its notes, time entries, attachment metadata and (opt-in) histories — every part separately reported. | get: Get one Ticket by id. | getContext: Get a ticket with its primary context records in one call. | getEntityInfo: Get the Ticket capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the Ticket field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getUdfInfo: Get the Ticket user-defined-field catalog. | query: Query Ticket records with a JSON filter. | resolve: Resolve a Ticket from a numeric id (the direct fetch, tried first) or an exact value of its primary text field(s) (title — exact match only). Returns the Ticket record with the numeric id for get/update/delete. Other identifier forms (e.g. an email address) are NOT tried by resolve — use the resource's dedicated lookup op where one exists (see autotask_catalog). | search: Search ticket records by text (contains on the searchable fields). | slaHealthCheck: Run the SLA health check for a ticket (numeric ticket id). | summary: Get a one-call summary of a ticket (numeric ticket id). | timeline: Get the event timeline of a ticket (numeric ticket id).. Bounds: Bounded: server-side pagination — up to limit records (default 500, max 2500) in one call; 1000-page guard; hasMore flags a truncation. Bounded: limit default 25, max 500. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.",
24
+ "description": "Read ticket records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for tickets: count: Count ticket records matching a JSON filter. | findByTicketNumber: Find a ticket by its ticketNumber. | fullContext: Get the full context of one ticket in a single call: the ticket, its company, contact, assigned resource and project, plus its notes, time entries, attachment metadata and (opt-in) histories — every part separately reported. | get: Get one Ticket by id. | getContext: Get a ticket with its primary context records in one call. | getEntityInfo: Get the Ticket capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the Ticket field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getUdfInfo: Get the Ticket user-defined-field catalog. | query: Query Ticket records with a JSON filter. | resolve: Resolve a Ticket from a numeric id (the direct fetch, tried first) or an exact value of its primary text field(s) (title — exact match only). Returns the Ticket record with the numeric id for get/update/delete. Other identifier forms (e.g. an email address) are NOT tried by resolve — use the resource's dedicated lookup op where one exists (see autotask_catalog). | search: Search ticket records by text (a vendor contains filter on title only — the vendor exposes no free-text search and no matched-field reporting). | slaHealthCheck: Run the SLA health check for a ticket (numeric ticket id). | summary: Get a one-call summary of a ticket (numeric ticket id). | timeline: Get the event timeline of a ticket (numeric ticket id).. Bounds: Bounded: server-side pagination — up to limit records (default 500, max 2500) in one call; 1000-page guard; hasMore flags a truncation. Bounded: limit default 25, max 500. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.",
25
25
  "annotations": {
26
26
  "readOnlyHint": true,
27
27
  "destructiveHint": false,
@@ -40,6 +40,11 @@
40
40
  "properties": {
41
41
  "filter": {
42
42
  "type": "Filter (13 operators + and/or; see docs/API.md)"
43
+ },
44
+ "fieldCheck": {
45
+ "type": "boolean",
46
+ "default": true,
47
+ "note": "false disables the pre-request name check on the filter field names (a non-boolean is refused pre-request; the shape/grammar checks always run); consumed by the invoke router — never sent to the vendor (issue #145)"
43
48
  }
44
49
  },
45
50
  "required": []
@@ -317,7 +322,7 @@
317
322
  "idempotentHint": true,
318
323
  "openWorldHint": true
319
324
  },
320
- "purpose": "Search ticket records by text (contains on the searchable fields).",
325
+ "purpose": "Search ticket records by text (a vendor contains filter on title only — the vendor exposes no free-text search and no matched-field reporting).",
321
326
  "bounds": "Bounded: limit default 25, max 500."
322
327
  },
323
328
  {
@@ -658,6 +663,11 @@
658
663
  "properties": {
659
664
  "filter": {
660
665
  "type": "Filter (13 operators + and/or; see docs/API.md)"
666
+ },
667
+ "fieldCheck": {
668
+ "type": "boolean",
669
+ "default": true,
670
+ "note": "false disables the pre-request name check on the filter field names (a non-boolean is refused pre-request; the shape/grammar checks always run); consumed by the invoke router — never sent to the vendor (issue #145)"
661
671
  }
662
672
  },
663
673
  "required": []
@@ -1201,6 +1211,11 @@
1201
1211
  "properties": {
1202
1212
  "filter": {
1203
1213
  "type": "Filter (13 operators + and/or; see docs/API.md)"
1214
+ },
1215
+ "fieldCheck": {
1216
+ "type": "boolean",
1217
+ "default": true,
1218
+ "note": "false disables the pre-request name check on the filter field names (a non-boolean is refused pre-request; the shape/grammar checks always run); consumed by the invoke router — never sent to the vendor (issue #145)"
1204
1219
  }
1205
1220
  },
1206
1221
  "required": []
@@ -1739,6 +1754,11 @@
1739
1754
  "properties": {
1740
1755
  "filter": {
1741
1756
  "type": "Filter (13 operators + and/or; see docs/API.md)"
1757
+ },
1758
+ "fieldCheck": {
1759
+ "type": "boolean",
1760
+ "default": true,
1761
+ "note": "false disables the pre-request name check on the filter field names (a non-boolean is refused pre-request; the shape/grammar checks always run); consumed by the invoke router — never sent to the vendor (issue #145)"
1742
1762
  }
1743
1763
  },
1744
1764
  "required": []
@@ -2108,6 +2128,11 @@
2108
2128
  "properties": {
2109
2129
  "filter": {
2110
2130
  "type": "Filter (13 operators + and/or; see docs/API.md)"
2131
+ },
2132
+ "fieldCheck": {
2133
+ "type": "boolean",
2134
+ "default": true,
2135
+ "note": "false disables the pre-request name check on the filter field names (a non-boolean is refused pre-request; the shape/grammar checks always run); consumed by the invoke router — never sent to the vendor (issue #145)"
2111
2136
  }
2112
2137
  },
2113
2138
  "required": []
@@ -2445,6 +2470,11 @@
2445
2470
  "properties": {
2446
2471
  "filter": {
2447
2472
  "type": "Filter (13 operators + and/or; see docs/API.md)"
2473
+ },
2474
+ "fieldCheck": {
2475
+ "type": "boolean",
2476
+ "default": true,
2477
+ "note": "false disables the pre-request name check on the filter field names (a non-boolean is refused pre-request; the shape/grammar checks always run); consumed by the invoke router — never sent to the vendor (issue #145)"
2448
2478
  }
2449
2479
  },
2450
2480
  "required": []
@@ -2830,7 +2860,7 @@
2830
2860
  "entity": "timeEntry",
2831
2861
  "apiEntity": "time_entries",
2832
2862
  "effectLine": "effect=read only — zero mutating requests, ungated by design",
2833
- "description": "Read timeEntry records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for time_entries: count: Count time entry records matching a JSON filter. | customRange: Get time entries for an explicit dateWorked range. | dateWorkedWindow: The clearer-named alias for time_entries.postedWithin (issue #66): the dateWorked window read with the posted/unposted split, counted in one pass — same wire, same result (incl. truncated). | get: Get one TimeEntry by id. | getEntityInfo: Get the TimeEntry capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the TimeEntry field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getPosted: Get posted time entries (optionally for one ticket). | getUdfInfo: Get the TimeEntry user-defined-field catalog. | getUnposted: Get unposted time entries (optionally for one ticket). | last14: Get time entries for the last 14 days (rolling, dateWorked). | last24h: Get time entries for the last 24 hours (rolling, dateWorked). | last30: Get time entries for the last 30 days (rolling, dateWorked). | last7: Get time entries for the last 7 days (rolling, dateWorked). | last90: Get time entries for the last 90 days (rolling, dateWorked). | lastFullFortnight: Get time entries for the last complete fortnight. | lastFullMonth: Get time entries for the last complete calendar month. | lastFullQuarter: Get time entries for the last complete calendar quarter. | lastFullWeek: Get time entries for the last complete calendar week. | postedWithin: Get time entries worked within an inclusive dateWorked window with the posted/unposted split, counted in one pass (the window is ON the dateWorked field — the posted narrowing is a server-side postedDate exist|notExist predicate; the counts cover the returned rows only, truncated flags the capped window). | query: Query TimeEntry records with a JSON filter. | resolve: Resolve a time entry from a numeric id (the direct fetch) only — this resource has no searchable text field, so a non-numeric identifier throws a config error. | today: Get time entries for today (dateWorked = now, server date). | yesterday: Get time entries for yesterday (dateWorked = yesterday, server date).. Bounds: Bounded: server-side pagination — up to limit records (default 500, max 2500) in one call; 1000-page guard; hasMore flags a truncation. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.",
2863
+ "description": "Read timeEntry records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for time_entries: count: Count time entry records matching a JSON filter. | customRange: Get time entries for an explicit dateWorked range. | dateWorkedWindow: The unambiguous-named alias for time_entries.postedWithin (issue #66): the dateWorked window read with the posted/unposted split, counted in one pass — same wire, same result (incl. truncated). The window is ON dateWorked (entries worked within [from, to]); \"postedWithin\" keeps its historical name (its \"posted\" is the posted/unposted split over the returned rows, never a posted-date window). | get: Get one TimeEntry by id. | getEntityInfo: Get the TimeEntry capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the TimeEntry field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getPosted: Get posted time entries (optionally for one ticket). | getUdfInfo: Get the TimeEntry user-defined-field catalog. | getUnposted: Get unposted time entries (optionally for one ticket). | last14: Get time entries for the last 14 days (rolling, dateWorked). | last24h: Get time entries for the last 24 hours (rolling, dateWorked). | last30: Get time entries for the last 30 days (rolling, dateWorked). | last7: Get time entries for the last 7 days (rolling, dateWorked). | last90: Get time entries for the last 90 days (rolling, dateWorked). | lastFullFortnight: Get time entries for the last complete fortnight. | lastFullMonth: Get time entries for the last complete calendar month. | lastFullQuarter: Get time entries for the last complete calendar quarter. | lastFullWeek: Get time entries for the last complete calendar week. | postedWithin: Get time entries WORKED within an inclusive dateWorked window with the posted/unposted split, counted in one pass. The window is ON the dateWorked field — the name's \"posted\" is the posted/unposted SPLIT counted over the returned rows (the server-side postedDate exist|notExist predicate), never a posted-date window: no postedDate window op exists (the vendored TimeEntryModel declares no postedDate filter, and ww29-class zones 500 on postedDate filters ONLY WHEN the `posted` narrowing is set — omit `posted` on such zones — W-D3). The counts cover the returned rows only; truncated flags the capped window. time_entries.dateWorkedWindow is the unambiguous name. | query: Query TimeEntry records with a JSON filter. | resolve: Resolve a time entry from a numeric id (the direct fetch) only — this resource has no searchable text field, so a non-numeric identifier throws a config error. | today: Get time entries for today (dateWorked = now, server date). | yesterday: Get time entries for yesterday (dateWorked = yesterday, server date).. Bounds: Bounded: server-side pagination — up to limit records (default 500, max 2500) in one call; 1000-page guard; hasMore flags a truncation. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.",
2834
2864
  "annotations": {
2835
2865
  "readOnlyHint": true,
2836
2866
  "destructiveHint": false,
@@ -2849,6 +2879,11 @@
2849
2879
  "properties": {
2850
2880
  "filter": {
2851
2881
  "type": "Filter (13 operators + and/or; see docs/API.md)"
2882
+ },
2883
+ "fieldCheck": {
2884
+ "type": "boolean",
2885
+ "default": true,
2886
+ "note": "false disables the pre-request name check on the filter field names (a non-boolean is refused pre-request; the shape/grammar checks always run); consumed by the invoke router — never sent to the vendor (issue #145)"
2852
2887
  }
2853
2888
  },
2854
2889
  "required": []
@@ -2882,7 +2917,7 @@
2882
2917
  },
2883
2918
  "limit": {
2884
2919
  "type": "number",
2885
- "default": 25,
2920
+ "default": 500,
2886
2921
  "max": 500
2887
2922
  }
2888
2923
  },
@@ -2916,7 +2951,7 @@
2916
2951
  "type": "string (UTC date, inclusive — the dateWorked window start: the from-day start; when opts.tz is given the LOCAL calendar day in that zone, converted to UTC)"
2917
2952
  },
2918
2953
  "to": {
2919
- "type": "string (UTC date, inclusive — the dateWorked window end: the to-day end; when opts.tz is given both bounds are LOCAL calendar days in that zone, converted to UTC). The clearer-named alias for postedWithin (same wire, same result incl. truncated — the name names the dateWorked window the counts cover). Result { items, postedCount, unpostedCount, window, truncated } — the counts cover the returned rows only (at most limit)"
2954
+ "type": "string (UTC date, inclusive — the dateWorked window end: the to-day end; when opts.tz is given both bounds are LOCAL calendar days in that zone, converted to UTC). The unambiguous-named alias for postedWithin (same wire, same result incl. truncated — the name names the dateWorked window the counts cover). Result { items, postedCount, unpostedCount, window, truncated } — the counts cover the returned rows only (at most limit)"
2920
2955
  }
2921
2956
  },
2922
2957
  "required": [
@@ -2930,7 +2965,7 @@
2930
2965
  "idempotentHint": true,
2931
2966
  "openWorldHint": true
2932
2967
  },
2933
- "purpose": "The clearer-named alias for time_entries.postedWithin (issue #66): the dateWorked window read with the posted/unposted split, counted in one pass — same wire, same result (incl. truncated).",
2968
+ "purpose": "The unambiguous-named alias for time_entries.postedWithin (issue #66): the dateWorked window read with the posted/unposted split, counted in one pass — same wire, same result (incl. truncated). The window is ON dateWorked (entries worked within [from, to]); \"postedWithin\" keeps its historical name (its \"posted\" is the posted/unposted split over the returned rows, never a posted-date window).",
2934
2969
  "bounds": ""
2935
2970
  },
2936
2971
  {
@@ -3271,7 +3306,7 @@
3271
3306
  "idempotentHint": true,
3272
3307
  "openWorldHint": true
3273
3308
  },
3274
- "purpose": "Get time entries worked within an inclusive dateWorked window with the posted/unposted split, counted in one pass (the window is ON the dateWorked field — the posted narrowing is a server-side postedDate exist|notExist predicate; the counts cover the returned rows only, truncated flags the capped window).",
3309
+ "purpose": "Get time entries WORKED within an inclusive dateWorked window with the posted/unposted split, counted in one pass. The window is ON the dateWorked field — the name's \"posted\" is the posted/unposted SPLIT counted over the returned rows (the server-side postedDate exist|notExist predicate), never a posted-date window: no postedDate window op exists (the vendored TimeEntryModel declares no postedDate filter, and ww29-class zones 500 on postedDate filters ONLY WHEN the `posted` narrowing is set — omit `posted` on such zones — W-D3). The counts cover the returned rows only; truncated flags the capped window. time_entries.dateWorkedWindow is the unambiguous name.",
3275
3310
  "bounds": ""
3276
3311
  },
3277
3312
  {
@@ -3667,6 +3702,6 @@
3667
3702
  "approvalGatedWrites": 2,
3668
3703
  "destructiveExcluded": 3
3669
3704
  },
3670
- "capabilitiesHash": "d30f65bc49ac2898667865b753c0f0cd3f0c951c337a1036b48b049e8c32c61a",
3705
+ "capabilitiesHash": "1fdd74785554c692c5dbe41343b915bee0c2eafc1f88614e79a2c8dcc2d6ed76",
3671
3706
  "curationHash": "e19117ce73e38d9aded3be7e7f077a4e185d2e10294622b76c17d67f63cf9e3b"
3672
3707
  }
@@ -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-19T05:26:26.775Z — do not hand-edit. -->
3
+ <!-- GENERATED by scripts/project-n8n-mcp.mjs at 2026-10-20T01:49:50.705Z — 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
@@ -40,7 +40,7 @@ user's n8n instances; PROD is read-only for any verification).
40
40
 
41
41
  ### `autotask_ticket` (read — ticket)
42
42
 
43
- > Read ticket records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for tickets: count: Count ticket records matching a JSON filter. | findByTicketNumber: Find a ticket by its ticketNumber. | fullContext: Get the full context of one ticket in a single call: the ticket, its company, contact, assigned resource and project, plus its notes, time entries, attachment metadata and (opt-in) histories — every part separately reported. | get: Get one Ticket by id. | getContext: Get a ticket with its primary context records in one call. | getEntityInfo: Get the Ticket capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the Ticket field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getUdfInfo: Get the Ticket user-defined-field catalog. | query: Query Ticket records with a JSON filter. | resolve: Resolve a Ticket from a numeric id (the direct fetch, tried first) or an exact value of its primary text field(s) (title — exact match only). Returns the Ticket record with the numeric id for get/update/delete. Other identifier forms (e.g. an email address) are NOT tried by resolve — use the resource's dedicated lookup op where one exists (see autotask_catalog). | search: Search ticket records by text (contains on the searchable fields). | slaHealthCheck: Run the SLA health check for a ticket (numeric ticket id). | summary: Get a one-call summary of a ticket (numeric ticket id). | timeline: Get the event timeline of a ticket (numeric ticket id).. Bounds: Bounded: server-side pagination — up to limit records (default 500, max 2500) in one call; 1000-page guard; hasMore flags a truncation. Bounded: limit default 25, max 500. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
43
+ > Read ticket records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for tickets: count: Count ticket records matching a JSON filter. | findByTicketNumber: Find a ticket by its ticketNumber. | fullContext: Get the full context of one ticket in a single call: the ticket, its company, contact, assigned resource and project, plus its notes, time entries, attachment metadata and (opt-in) histories — every part separately reported. | get: Get one Ticket by id. | getContext: Get a ticket with its primary context records in one call. | getEntityInfo: Get the Ticket capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the Ticket field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getUdfInfo: Get the Ticket user-defined-field catalog. | query: Query Ticket records with a JSON filter. | resolve: Resolve a Ticket from a numeric id (the direct fetch, tried first) or an exact value of its primary text field(s) (title — exact match only). Returns the Ticket record with the numeric id for get/update/delete. Other identifier forms (e.g. an email address) are NOT tried by resolve — use the resource's dedicated lookup op where one exists (see autotask_catalog). | search: Search ticket records by text (a vendor contains filter on title only — the vendor exposes no free-text search and no matched-field reporting). | slaHealthCheck: Run the SLA health check for a ticket (numeric ticket id). | summary: Get a one-call summary of a ticket (numeric ticket id). | timeline: Get the event timeline of a ticket (numeric ticket id).. Bounds: Bounded: server-side pagination — up to limit records (default 500, max 2500) in one call; 1000-page guard; hasMore flags a truncation. Bounded: limit default 25, max 500. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
44
44
 
45
45
  | backingOperation | verb | effect | flags | requiredArgs | purpose |
46
46
  |------------------|------|--------|-------|--------------|---------|
@@ -54,7 +54,7 @@ user's n8n instances; PROD is read-only for any verification).
54
54
  | `tickets.getUdfInfo` | getUdfInfo | read | — | none | Get the Ticket user-defined-field catalog. |
55
55
  | `tickets.query` | query | read | — | none | Query Ticket records with a JSON filter. Bounded: server-side pagination — up to limit records (default 500, max 2500) in one call; 1000-page guard; hasMore flags a truncation. |
56
56
  | `tickets.resolve` | resolve | read | — | identifier | Resolve a Ticket from a numeric id (the direct fetch, tried first) or an exact value of its primary text field(s) (title — exact match only). Returns the Ticket record with the numeric id for get/update/delete. Other identifier forms (e.g. an email address) are NOT tried by resolve — use the resource's dedicated lookup op where one exists (see autotask_catalog). |
57
- | `tickets.search` | search | read | — | query | Search ticket records by text (contains on the searchable fields). Bounded: limit default 25, max 500. |
57
+ | `tickets.search` | search | read | — | query | Search ticket records by text (a vendor contains filter on title only — the vendor exposes no free-text search and no matched-field reporting). Bounded: limit default 25, max 500. |
58
58
  | `tickets.slaHealthCheck` | slaHealthCheck | read | — | id | Run the SLA health check for a ticket (numeric ticket id). |
59
59
  | `tickets.summary` | summary | read | — | id | Get a one-call summary of a ticket (numeric ticket id). |
60
60
  | `tickets.timeline` | timeline | read | — | id | Get the event timeline of a ticket (numeric ticket id). |
@@ -214,13 +214,13 @@ Tool input: {"operation":{"type":"string","enum":["transferOwnership","update"]}
214
214
 
215
215
  ### `autotask_timeEntry` (read — timeEntry)
216
216
 
217
- > Read timeEntry records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for time_entries: count: Count time entry records matching a JSON filter. | customRange: Get time entries for an explicit dateWorked range. | dateWorkedWindow: The clearer-named alias for time_entries.postedWithin (issue #66): the dateWorked window read with the posted/unposted split, counted in one pass — same wire, same result (incl. truncated). | get: Get one TimeEntry by id. | getEntityInfo: Get the TimeEntry capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the TimeEntry field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getPosted: Get posted time entries (optionally for one ticket). | getUdfInfo: Get the TimeEntry user-defined-field catalog. | getUnposted: Get unposted time entries (optionally for one ticket). | last14: Get time entries for the last 14 days (rolling, dateWorked). | last24h: Get time entries for the last 24 hours (rolling, dateWorked). | last30: Get time entries for the last 30 days (rolling, dateWorked). | last7: Get time entries for the last 7 days (rolling, dateWorked). | last90: Get time entries for the last 90 days (rolling, dateWorked). | lastFullFortnight: Get time entries for the last complete fortnight. | lastFullMonth: Get time entries for the last complete calendar month. | lastFullQuarter: Get time entries for the last complete calendar quarter. | lastFullWeek: Get time entries for the last complete calendar week. | postedWithin: Get time entries worked within an inclusive dateWorked window with the posted/unposted split, counted in one pass (the window is ON the dateWorked field — the posted narrowing is a server-side postedDate exist|notExist predicate; the counts cover the returned rows only, truncated flags the capped window). | query: Query TimeEntry records with a JSON filter. | resolve: Resolve a time entry from a numeric id (the direct fetch) only — this resource has no searchable text field, so a non-numeric identifier throws a config error. | today: Get time entries for today (dateWorked = now, server date). | yesterday: Get time entries for yesterday (dateWorked = yesterday, server date).. Bounds: Bounded: server-side pagination — up to limit records (default 500, max 2500) in one call; 1000-page guard; hasMore flags a truncation. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
217
+ > Read timeEntry records (mode: read — effect=read operations only; zero mutating requests, ungated by design). Registry operations for time_entries: count: Count time entry records matching a JSON filter. | customRange: Get time entries for an explicit dateWorked range. | dateWorkedWindow: The unambiguous-named alias for time_entries.postedWithin (issue #66): the dateWorked window read with the posted/unposted split, counted in one pass — same wire, same result (incl. truncated). The window is ON dateWorked (entries worked within [from, to]); "postedWithin" keeps its historical name (its "posted" is the posted/unposted split over the returned rows, never a posted-date window). | get: Get one TimeEntry by id. | getEntityInfo: Get the TimeEntry capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). | getFieldInfo: Get the TimeEntry field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). | getPosted: Get posted time entries (optionally for one ticket). | getUdfInfo: Get the TimeEntry user-defined-field catalog. | getUnposted: Get unposted time entries (optionally for one ticket). | last14: Get time entries for the last 14 days (rolling, dateWorked). | last24h: Get time entries for the last 24 hours (rolling, dateWorked). | last30: Get time entries for the last 30 days (rolling, dateWorked). | last7: Get time entries for the last 7 days (rolling, dateWorked). | last90: Get time entries for the last 90 days (rolling, dateWorked). | lastFullFortnight: Get time entries for the last complete fortnight. | lastFullMonth: Get time entries for the last complete calendar month. | lastFullQuarter: Get time entries for the last complete calendar quarter. | lastFullWeek: Get time entries for the last complete calendar week. | postedWithin: Get time entries WORKED within an inclusive dateWorked window with the posted/unposted split, counted in one pass. The window is ON the dateWorked field — the name's "posted" is the posted/unposted SPLIT counted over the returned rows (the server-side postedDate exist|notExist predicate), never a posted-date window: no postedDate window op exists (the vendored TimeEntryModel declares no postedDate filter, and ww29-class zones 500 on postedDate filters ONLY WHEN the `posted` narrowing is set — omit `posted` on such zones — W-D3). The counts cover the returned rows only; truncated flags the capped window. time_entries.dateWorkedWindow is the unambiguous name. | query: Query TimeEntry records with a JSON filter. | resolve: Resolve a time entry from a numeric id (the direct fetch) only — this resource has no searchable text field, so a non-numeric identifier throws a config error. | today: Get time entries for today (dateWorked = now, server date). | yesterday: Get time entries for yesterday (dateWorked = yesterday, server date).. Bounds: Bounded: server-side pagination — up to limit records (default 500, max 2500) in one call; 1000-page guard; hasMore flags a truncation. Errors carry the SDK error contract: code, category, retryable, operation, httpStatus, suggestedAction (the nextAction directive — follow the directive), correlationId, vendorError (when the vendor returned a response body — the deep-redacted body, host-sourced text wrapped as untrusted content when untrusted-wrapping is on); credentials never appear in an error message.
218
218
 
219
219
  | backingOperation | verb | effect | flags | requiredArgs | purpose |
220
220
  |------------------|------|--------|-------|--------------|---------|
221
221
  | `time_entries.count` | count | read | — | none | Count time entry records matching a JSON filter. |
222
222
  | `time_entries.customRange` | customRange | read | — | from, to | Get time entries for an explicit dateWorked range. |
223
- | `time_entries.dateWorkedWindow` | dateWorkedWindow | read | — | from, to | The clearer-named alias for time_entries.postedWithin (issue #66): the dateWorked window read with the posted/unposted split, counted in one pass — same wire, same result (incl. truncated). |
223
+ | `time_entries.dateWorkedWindow` | dateWorkedWindow | read | — | from, to | The unambiguous-named alias for time_entries.postedWithin (issue #66): the dateWorked window read with the posted/unposted split, counted in one pass — same wire, same result (incl. truncated). The window is ON dateWorked (entries worked within [from, to]); "postedWithin" keeps its historical name (its "posted" is the posted/unposted split over the returned rows, never a posted-date window). |
224
224
  | `time_entries.get` | get | read | — | id | Get one TimeEntry by id. |
225
225
  | `time_entries.getEntityInfo` | getEntityInfo | read | — | none | Get the TimeEntry capability metadata (canCreate/canUpdate/hasUserDefinedFields/...). |
226
226
  | `time_entries.getFieldInfo` | getFieldInfo | read | — | none | Get the TimeEntry field catalog (types, picklists, required, maxLength) — the full raw field dump (the largest read of the info trio; compact discovery: operations.fieldCatalog). |
@@ -236,7 +236,7 @@ Tool input: {"operation":{"type":"string","enum":["transferOwnership","update"]}
236
236
  | `time_entries.lastFullMonth` | lastFullMonth | read | — | none | Get time entries for the last complete calendar month. |
237
237
  | `time_entries.lastFullQuarter` | lastFullQuarter | read | — | none | Get time entries for the last complete calendar quarter. |
238
238
  | `time_entries.lastFullWeek` | lastFullWeek | read | — | none | Get time entries for the last complete calendar week. |
239
- | `time_entries.postedWithin` | postedWithin | read | — | from, to | Get time entries worked within an inclusive dateWorked window with the posted/unposted split, counted in one pass (the window is ON the dateWorked field — the posted narrowing is a server-side postedDate exist|notExist predicate; the counts cover the returned rows only, truncated flags the capped window). |
239
+ | `time_entries.postedWithin` | postedWithin | read | — | from, to | Get time entries WORKED within an inclusive dateWorked window with the posted/unposted split, counted in one pass. The window is ON the dateWorked field — the name's "posted" is the posted/unposted SPLIT counted over the returned rows (the server-side postedDate exist|notExist predicate), never a posted-date window: no postedDate window op exists (the vendored TimeEntryModel declares no postedDate filter, and ww29-class zones 500 on postedDate filters ONLY WHEN the `posted` narrowing is set — omit `posted` on such zones — W-D3). The counts cover the returned rows only; truncated flags the capped window. time_entries.dateWorkedWindow is the unambiguous name. |
240
240
  | `time_entries.query` | query | read | — | none | Query TimeEntry records with a JSON filter. Bounded: server-side pagination — up to limit records (default 500, max 2500) in one call; 1000-page guard; hasMore flags a truncation. |
241
241
  | `time_entries.resolve` | resolve | read | — | identifier | Resolve a time entry from a numeric id (the direct fetch) only — this resource has no searchable text field, so a non-numeric identifier throws a config error. |
242
242
  | `time_entries.today` | today | read | — | none | Get time entries for today (dateWorked = now, server date). |
@@ -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-19T05:26:26.775Z
286
- - capabilitiesHash: d30f65bc49ac2898667865b753c0f0cd3f0c951c337a1036b48b049e8c32c61a
285
+ - generatedAt: 2026-10-20T01:49:50.705Z
286
+ - capabilitiesHash: 1fdd74785554c692c5dbe41343b915bee0c2eafc1f88614e79a2c8dcc2d6ed76
287
287
  - curationHash: e19117ce73e38d9aded3be7e7f077a4e185d2e10294622b76c17d67f63cf9e3b
package/README.md CHANGED
@@ -434,10 +434,14 @@ The SDK ships the mechanical substrate, not a server (no MCP imports, no zod):
434
434
  whose `Content-Type` is not a JSON media type — the vendor maintenance
435
435
  page; `details` carries the content-type + a 256-byte truncated snippet
436
436
  that the MCP servers wrap as untrusted host text; a maintenance notice is waited
437
- out for up to `maintenanceMaxWaitMs` (default 1 h — the weekly window is usually
438
- under 30 min) for reads and writes, otherwise a `GET` or an explicitly
439
- `idempotent`-flagged call retries and a write makes ONE call and says the write
440
- may have taken effect) and `EMPTY_RESPONSE` (a blank 2xx on a call that expects a result
437
+ out for up to `maintenanceMaxWaitMs` (default 1 h; `MAINTENANCE_WINDOW_MAX_MS`
438
+ = 4 h, the observed ceiling) for reads and writes; the same wait recognises the
439
+ page at ANY status — `2xx`/`4xx`/`5xx`, issue #114 — but a `4xx`/`5xx` page is
440
+ waited out only for calls safe to re-send (a `GET` or an explicitly
441
+ `idempotent`-flagged call): an unflagged mutation makes ONE send and says the
442
+ write may have taken effect (`details.maintenanceDetected`); a non-matching
443
+ HTML-ish page stamps `details.maintenancePatternMiss`; otherwise a `GET` or an
444
+ explicitly `idempotent`-flagged call retries and a write makes ONE call) and `EMPTY_RESPONSE` (a blank 2xx on a call that expects a result
441
445
  — `expectBody`, non-DELETE by default; `DELETE` / 204 keep `undefined` and
442
446
  the delete ops synthesize `{ id }` from the path id on a blank answer). A
443
447
  vendor field-validation 500 matching the documented phrases (`exceeds