node-autotask 0.20.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +44 -21
- package/CHANGELOG.md +42 -0
- package/MCP_TOOL_CATALOG.json +1458 -12
- package/MCP_TOOL_MANIFEST.md +23 -20
- package/MCP_TOOL_OVERRIDES.json +7 -7
- package/N8N_MCP_MANIFEST.json +20 -4
- package/N8N_MCP_MANIFEST.md +6 -5
- package/README.md +9 -9
- package/capabilities.json +12362 -7886
- package/capabilities.schema.json +33 -12
- package/dist/capabilities.cjs +2 -2
- package/dist/capabilities.d.cts +9 -2
- package/dist/capabilities.d.ts +9 -2
- package/dist/capabilities.js +1 -1
- package/dist/{chunk-GA6UCX3Z.js → chunk-B5SNZ3TZ.js} +3507 -3113
- package/dist/{chunk-L6V66V22.cjs → chunk-D5TVBI4U.cjs} +3039 -2645
- package/dist/{chunk-FLYTKJKV.cjs → chunk-JUO3URUI.cjs} +12361 -7885
- package/dist/{chunk-YH5CPRDN.cjs → chunk-N27NUB4R.cjs} +2466 -1745
- package/dist/{chunk-2T77J2ZM.js → chunk-OGNUI3UR.js} +12361 -7885
- package/dist/{chunk-XSYJB4IW.js → chunk-PVUUOM5G.js} +2092 -1371
- package/dist/{chunk-RMLGOCGA.cjs → chunk-TTQB537V.cjs} +135 -135
- package/dist/{chunk-JEJUFXHX.js → chunk-VYFO2N5F.js} +2 -2
- package/dist/{index-BXtKN1M5.d.cts → index-BVtycq-6.d.cts} +1 -1
- package/dist/{index-BXtKN1M5.d.ts → index-BVtycq-6.d.ts} +1 -1
- package/dist/{index-AN-NjLi9.d.cts → index-DBoX-Dnk.d.ts} +430 -952
- package/dist/{index-CvLLsOCP.d.ts → index-NC-x-bYl.d.cts} +430 -952
- package/dist/index.cjs +91 -62
- package/dist/index.d.cts +360 -5
- package/dist/index.d.ts +360 -5
- package/dist/index.js +74 -45
- package/dist/operations/index.cjs +462 -462
- package/dist/operations/index.d.cts +78 -84
- package/dist/operations/index.d.ts +78 -84
- package/dist/operations/index.js +382 -382
- package/dist/pool.cjs +8 -8
- package/dist/pool.d.cts +2 -2
- package/dist/pool.d.ts +2 -2
- package/dist/pool.js +4 -4
- package/dist/resources/index.cjs +4 -4
- package/dist/resources/index.d.cts +2 -2
- package/dist/resources/index.d.ts +2 -2
- package/dist/resources/index.js +3 -3
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/docs/API.md +140 -45
- package/docs/N8N-ADAPTER.md +3 -3
- package/examples/mcp/errors.ts +135 -0
- package/examples/mcp/manifest.ts +168 -0
- package/examples/mcp/register.ts +66 -0
- package/examples/mcp/schemas.ts +203 -0
- package/examples/mcp/tools-meta.ts +120 -0
- package/examples/mcp/tools-mode.ts +177 -0
- package/examples/mcp/tools-read.ts +264 -0
- package/examples/mcp/types.ts +34 -0
- package/examples/mcp-register.ts +9 -1074
- package/package.json +1 -1
package/ARCHITECTURE.md
CHANGED
|
@@ -64,25 +64,28 @@ proves the mapping op-by-op.
|
|
|
64
64
|
src/
|
|
65
65
|
index.ts # Public API barrel (default + named exports)
|
|
66
66
|
client.ts # AutotaskClient — top-level facade; wires all resources + operations
|
|
67
|
-
config.ts #
|
|
68
|
-
http.ts # HttpClient
|
|
69
|
-
# envelope unwrap helpers
|
|
67
|
+
config.ts # barrel -> config/ (types, zones + zoneUrl(), defaults, profiles, resolve/validation)
|
|
68
|
+
http.ts # barrel -> http/ (HttpClient.ts = fetch transport; request-builders.ts = URL/query
|
|
69
|
+
# building + POST/GET switch; envelope.ts = unwrap helpers pinned to the G1
|
|
70
|
+
# envelopes; retry.ts, breaker-budget.ts, zone.ts, credentials.ts, body.ts, types.ts)
|
|
70
71
|
auth.ts # 3-header auth (ApiIntegrationCode/UserName/Secret) + ImpersonationResourceId
|
|
71
72
|
# + authenticate() token alternative; credential redaction list
|
|
72
|
-
errors.ts #
|
|
73
|
+
errors.ts # barrel -> errors/ (base.ts hierarchy root, http.ts transport errors, mutation.ts,
|
|
74
|
+
# from-status.ts errorFromStatus, vendor-errors.ts vendor error-body parsing)
|
|
73
75
|
pagination.ts # Page<T>, cursor-follow walk (nextPageUrl), collectAll(), toArray()
|
|
74
76
|
query.ts # Filter builder: 13 operators + and/or grouping, caps, GET<=2048 measurement
|
|
75
77
|
logger.ts # Logger interface + NoopLogger
|
|
76
|
-
utils.ts
|
|
78
|
+
utils/index.ts # assertRecord, deep-merge of params, identifier helpers, UDF flatten
|
|
77
79
|
capabilities.ts # GENERATED capability registry (./capabilities) — never hand-edited
|
|
78
80
|
operations/
|
|
79
81
|
index.ts # ./operations barrel
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
invoke.ts
|
|
82
|
+
searchAcrossResources.ts # cross-resource bounded text search
|
|
83
|
+
resolveAny.ts # cross-resource identifier resolution
|
|
84
|
+
invoke.ts + invoke/ # registry-driven invoke + write governor (dispatch, validate, modes, wrappers, ...)
|
|
85
|
+
... # aggregate, fetchMany, fetchByIds, recovery, relationshipMap, probeReachability, ...
|
|
83
86
|
resources/
|
|
84
87
|
index.ts # Barrel for all resource classes + resources() accessor
|
|
85
|
-
base.ts
|
|
88
|
+
base.ts + base/ # BaseResource<T> (base/BaseResource.ts; query/mutations/meta/helpers split) — shared query/get/create/update/delete scaffolding,
|
|
86
89
|
# entityInfo ops, dry-run scaffolding, scan guards
|
|
87
90
|
companies.ts # CompaniesResource (top-level entity template — see §12.1)
|
|
88
91
|
company_notes.ts # CompanyNotesResource (parent-scoped child template — see §12.2)
|
|
@@ -99,12 +102,14 @@ src/
|
|
|
99
102
|
test/
|
|
100
103
|
__fixtures__/ # Mock JSON responses per resource (derived from api-docs.json definitions)
|
|
101
104
|
client.test.ts # AutotaskClient construction + wiring
|
|
102
|
-
http
|
|
105
|
+
http/ # one file per concern: transport, envelope, retry, query-builders, hardening,
|
|
106
|
+
# concurrency, logging, mutation-retry
|
|
103
107
|
pagination.test.ts # cursor-follow walk (nextPageUrl), 1000-page guard
|
|
104
108
|
query.test.ts # filter builder, caps, GET-length fallback
|
|
105
109
|
config.test.ts # zone/baseUrl/credentials/pageSize validation
|
|
106
110
|
auth.test.ts # header shape, impersonation, redaction
|
|
107
111
|
errors.test.ts # error hierarchy + vendor error-body parsing + item:null semantics
|
|
112
|
+
# (plus errors-*.test.ts and transport-*.test.ts concern files)
|
|
108
113
|
operations/ # searchAcrossResources / resolveAny tests
|
|
109
114
|
resources/
|
|
110
115
|
companies.test.ts # one spec-per-resource file, alongside each resource
|
|
@@ -273,7 +278,7 @@ shape only — deliberately.** Do not "fix" this without reading the reason:
|
|
|
273
278
|
|
|
274
279
|
## 6. Config, validation, and defaults
|
|
275
280
|
|
|
276
|
-
### `config
|
|
281
|
+
### `src/config/`
|
|
277
282
|
```ts
|
|
278
283
|
export type ZoneName =
|
|
279
284
|
| 'pre-release' | 'pre-release-uk' | 'limited-release' | 'limited-release-uk'
|
|
@@ -484,7 +489,7 @@ Vendor filter structure (swagger `Filter`, recursive):
|
|
|
484
489
|
|
|
485
490
|
---
|
|
486
491
|
|
|
487
|
-
## 8. HTTP transport (`http
|
|
492
|
+
## 8. HTTP transport (`src/http/`)
|
|
488
493
|
|
|
489
494
|
- Uses the **native global `fetch`** (native since Node 18; this package's floor is Node >= 24). **Zero runtime dependencies** across the
|
|
490
495
|
whole package.
|
|
@@ -571,18 +576,18 @@ export interface RequestOptions {
|
|
|
571
576
|
auto-unwrap — envelope unwrapping is an explicit per-call decision made by the resource
|
|
572
577
|
layer using the helpers below, pinned to the G1 envelopes.
|
|
573
578
|
10. **Redirects are never followed**: every request is sent with `redirect: 'manual'`
|
|
574
|
-
(`
|
|
579
|
+
(`HttpClient.ts`; the zone-discovery call in `src/http/zone.ts` likewise, with the same comment). A
|
|
575
580
|
`3xx` on a normal request cancels the body and throws immediately — no second request
|
|
576
581
|
is made:
|
|
577
582
|
- error: `ResponseRejectedError` with `code: 'REDIRECT_BLOCKED'`, `category: 'policy'`,
|
|
578
583
|
`retryable: false`, `httpStatus` = the 3xx status, and `suggestedAction: 'Check the
|
|
579
|
-
configured zone/baseUrl; the API does not redirect.'` (`
|
|
580
|
-
|
|
584
|
+
configured zone/baseUrl; the API does not redirect.'` (`ResponseRejectedError` in
|
|
585
|
+
`src/errors/http.ts`; the throw site is in `src/http/HttpClient.ts`).
|
|
581
586
|
- why: **this SDK attaches credentials to every request**, and a native redirect
|
|
582
587
|
follow re-sends those headers to the redirect target — even cross-origin. Wrapping
|
|
583
588
|
the refusal in a typed error is deliberate (issue #27 shape).
|
|
584
589
|
- the `Location` header is never read, so an attacker-supplied target cannot reach the
|
|
585
|
-
caller through the message (`test/http.test.ts
|
|
590
|
+
caller through the message (`test/http/hardening.test.ts` — "never follows redirects:
|
|
586
591
|
sends redirect `'manual'` and refuses a 3xx without a second request" — pins exactly
|
|
587
592
|
one wire call, `redirect: 'manual'`, `code: 'REDIRECT_BLOCKED'`, `retryable: false`,
|
|
588
593
|
and no `evil.example` in the text). Shipped in v0.3.1 (`CHANGELOG.md`); the same
|
|
@@ -597,7 +602,7 @@ export interface RequestOptions {
|
|
|
597
602
|
redirect resolved, and the skill's `manualRedirect` escape hatch (only for endpoints
|
|
598
603
|
whose contract IS a redirect) does not apply here; the builder does not implement one.
|
|
599
604
|
|
|
600
|
-
### Envelope unwrapping helpers (in `http.ts`, used by `BaseResource`)
|
|
605
|
+
### Envelope unwrapping helpers (in `src/http/envelope.ts`, used by `BaseResource`)
|
|
601
606
|
```ts
|
|
602
607
|
/** query: expects { items: T[], pageDetails: PagingModel }; validates both present. */
|
|
603
608
|
export function unwrapItems<T>(body: unknown): { items: T[]; pageDetails: PagingModel };
|
|
@@ -669,7 +674,7 @@ export function withAuthHeaders(
|
|
|
669
674
|
|
|
670
675
|
---
|
|
671
676
|
|
|
672
|
-
## 10. Error hierarchy (`errors
|
|
677
|
+
## 10. Error hierarchy (`src/errors/`)
|
|
673
678
|
|
|
674
679
|
```
|
|
675
680
|
AutotaskError (base; extends Error)
|
|
@@ -1000,7 +1005,7 @@ export const NoopLogger: Logger = {};
|
|
|
1000
1005
|
|
|
1001
1006
|
## 16. Testing strategy
|
|
1002
1007
|
|
|
1003
|
-
- **HTTP layer (`http
|
|
1008
|
+
- **HTTP layer (`test/http/*.test.ts`)**: mocked global `fetch` — URL construction, auth headers
|
|
1004
1009
|
(swagger casing + impersonation), POST/GET query switch (incl. 2048-char `QUERY_TOO_LONG`),
|
|
1005
1010
|
envelope pass-through, per-envelope unwrap errors, error mapping per status (vendor error
|
|
1006
1011
|
body formatting), retry/backoff on 429 with `Retry-After`, `Itgenatr005` as retryable
|
|
@@ -1022,7 +1027,7 @@ export const NoopLogger: Logger = {};
|
|
|
1022
1027
|
- **Coverage gate** (vitest config): lines >= 90 / functions >= 90 / branches >= 80 /
|
|
1023
1028
|
statements >= 90 (coordinator default from SCOPING F; node-hudu's 97/94/83/97 is the
|
|
1024
1029
|
stretch target — ratchet at v0.2 once generated-resource tests settle).
|
|
1025
|
-
- **Additive-only surface proof**: `
|
|
1030
|
+
- **Additive-only surface proof**: `scripts/public-surface.mjs` (exercised by `test/scripts/generated-gates.test.ts`) compares the live
|
|
1026
1031
|
exported surface against `test/__fixtures__/public-surface.json`, captured at the G3
|
|
1027
1032
|
baseline. Removing a baseline name fails the suite; additions are reported, not failed.
|
|
1028
1033
|
- **Capability gate**: `check-capabilities.mjs` ships with a committed negative fixture
|
|
@@ -1036,7 +1041,7 @@ Gates G3–G7 of `.run/CHECKLIST.md`; the per-batch loop never regenerates the w
|
|
|
1036
1041
|
until G6 (mechanism in `.run/design/generator-design.md` §10):
|
|
1037
1042
|
|
|
1038
1043
|
1. **G3 — Core + scaffold.** `package.json`/tsconfig/tsup/vitest/eslint; `logger.ts`,
|
|
1039
|
-
`errors
|
|
1044
|
+
`errors/`, `auth.ts`, `utils/` (no deps) → `config/` (+tests) → `http/`
|
|
1040
1045
|
(+tests) → `pagination.ts`, `query.ts` (+tests) → `types/common.ts` →
|
|
1041
1046
|
`resources/base.ts` → capture the public-surface fixture. Green:
|
|
1042
1047
|
`npm run typecheck && npm test && npm run build`.
|
|
@@ -1258,3 +1263,21 @@ What it ships instead:
|
|
|
1258
1263
|
`examples/mcp-server-http.ts` (SSE/streamable-HTTP transport). They are the reference
|
|
1259
1264
|
implementations the future Autotask MCP server package is built from; the SDK itself
|
|
1260
1265
|
remains MCP-import-free.
|
|
1266
|
+
|
|
1267
|
+
### 19.6 Composite moves (issue #197)
|
|
1268
|
+
|
|
1269
|
+
Some vendor entities accept an update of their parent field and silently keep the
|
|
1270
|
+
old value (live-probed on ww29: time entries and ticket charges; ticket notes DO
|
|
1271
|
+
move on a parent-scoped update). Where the entity can be deleted, a move is a
|
|
1272
|
+
composition on the shared engine (`special/_shared/move-engine.ts`): GET-only preview
|
|
1273
|
+
-> hard invariants -> twin check -> create (once) -> read-back compare -> source
|
|
1274
|
+
re-read -> delete (once), with rollback of the new record on any mismatch or drift.
|
|
1275
|
+
The engine returns only for `moved` / `moved_source_absent` / `moved_manual_delete`;
|
|
1276
|
+
every other outcome throws a typed `MoveRefusedError` carrying `details.status`
|
|
1277
|
+
(`failed` / `partial` / `unknown`). Entities the API cannot move or cannot finish
|
|
1278
|
+
deleting hand the person a `manualAction` with an `operations.uiLink` deep link
|
|
1279
|
+
(the record where ExecuteCommand supports it, else its parent ticket). The SDK
|
|
1280
|
+
authorises nothing (company scope and ownership are the caller's job) and stores
|
|
1281
|
+
nothing (the source snapshot is returned). A new resource joins by supplying a
|
|
1282
|
+
`MoveSpec` (copy set, invariants, verify list) and one plan row.
|
|
1283
|
+
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.23.0] - 2026-10-21
|
|
11
|
+
|
|
12
|
+
The plan hash moves (MINOR) — plan-pinned consumers must re-pin. The 0.23.0 plan hash is `95746a69c86d…` (recorded in `scripts/published-plan-hashes.json` after publish).
|
|
13
|
+
|
|
14
|
+
**W-A3 time-entry zone fact re-scoped (issue #195).** The 2026-10-06 note claimed the vendor "refuses ALL time-entry writes for this API-only user" (UNSATISFIABLE); live `time_entries.create` calls succeeded on 2026-10-21 (pair not recorded; two entries, read back, 1.0 h). The served wording (plan `pointerNotes` + `createGuidance` blocker message/remedy, the four time-entry tool-description overrides, `docs/API.md`) is now a (resource, role) pair-scoped wall: the pair selecting the API-only user's own resource was refused 2026-10-06. The authorised pair set has NOT been re-adjudicated by a live probe.
|
|
15
|
+
- Single source: the wording lives in `capabilities.plan.json`, regenerated into `capabilities.json`, `src/capabilities.ts`, `src/relationships.ts` and the MCP/n8n manifests and catalog.
|
|
16
|
+
- The plan records the success as `zoneFacts.resources.time_entries.liveWrites[]`; new gate clause 18 (`scripts/lib/checks/clause18-zone-fact-liveness.mjs`, test `test/scripts/zone-fact-liveness.test.ts`) fails when a plan zone fact (blocker message/remedy, pointer note) or an `MCP_TOOL_OVERRIDES.json` row asserts a wall (UNSATISFIABLE / refuses all, case-insensitive) stamped before a recorded successful live write of the same op.
|
|
17
|
+
- `scripts/s13b-plan-patch.mjs` no longer writes `pointerNotes` (its archived copy would overwrite the re-stamped rows with older-dated text).
|
|
18
|
+
- Invoke (#196): the query/count field-name refusal now embeds the entity's first 20 field names (`(and N more)` beyond that) in `err.message`, and its suggestedAction points at `operations.fieldCatalog` scoped with `resources: ["<entity>"]`; no extra wire. No plan-hash change.
|
|
19
|
+
|
|
20
|
+
### Added (wave-1: #197, #198, #200, #201, #202)
|
|
21
|
+
|
|
22
|
+
- Governed create-verify-delete moves (#197): `time_entries.moveToTicket` and `ticket_charges.moveToTicket` on a shared move engine, because the vendor accepts an update of `ticketID` but silently keeps the old value (live-probed). Dry-run-first (GET-only preview), `confirm` required, typed `MOVE_*` refusals (billed/approved/attachments/too old/twin/closed target), read-back compare, source re-read, rollback, results `moved` / `moved_source_absent` / `moved_manual_delete`, with a `manualAction` carrying an `operations.uiLink` deep link where a person must finish in the UI. New `MoveRefusedError`. Served hints now warn that `ticketID` does not persist on update and that deleting a time entry destroys its attachments; `ticket_notes` update documents that the scoped `ticketID` update moves a note. **Plan hash moves:** `bc4308db3dec…` → `95746a69c86d…` (MINOR; recorded after publish). Registry now 1216 ops / 853 tools.
|
|
23
|
+
- `companies.fullProfile` per-part field projection and `locationLimit` (#200): `fields: { contacts: ['id','firstName'], openTickets: [...] }` serves only the listed fields for a listed part (`IncludeFields` on the wire, then picked client-side); unlisted parts and the no-`fields` call are unchanged. Unknown part key or field name is refused pre-request (zero wire). `locationLimit` (1-500, default 50) replaces the fixed locations cap and drives `locations.capped`. Additive, minor; the plan hash is unchanged (the registry record and generated artifacts carry the new bag keys).
|
|
24
|
+
- `operations.uiLink` (#202): a pure, zero-wire meta op (plus the `buildUiLink` helper) that builds Autotask ExecuteCommand deep links for time entries, tickets (by id or ticket number), tasks, projects, companies, contacts, contracts and configuration items. The `ww<N>` UI host is derived from the client's `webservices<N>` base, never guessed (no link for `pre-release-de`/`-es` or custom hosts unless `host` is passed). Ids are positive safe integers, ticket numbers match `^T\d{8}\.\d{4}$`. **Plan hash moves** (new meta op row): `03a3bf610a85…` → `bc4308db3dec…` (MINOR; recorded after publish). Registry now 1214 ops / 851 tools.
|
|
25
|
+
- `operations.searchAcrossResources` accepts an optional `totalLimit` (#201): caps matches across all resources; resources are searched in request order, each asked for `min(limit, remaining)`, later ones are not queried and are reported in `notSearched`, with `truncated`. Absent `totalLimit` the result is unchanged. `limit` is now documented as per-resource (the old header said "one limit budget"). Additive, minor.
|
|
26
|
+
|
|
27
|
+
### Changed (wave-1: #198)
|
|
28
|
+
|
|
29
|
+
- Positional-arity refusal text (#198): dropped the v0.2.0 history aside, and when the surplus arg is a plain object on an op that declares an options bag it adds "options go inside the single `<bag>` object". Message text only; no behaviour or plan change.
|
|
30
|
+
|
|
31
|
+
## [0.22.0] - 2026-10-21
|
|
32
|
+
|
|
33
|
+
**Plan `tests[].file` paths refreshed (issues #194, #189).** The plan hash moves (MINOR) — plan-pinned consumers must re-pin. 111 `operations[].tests[]` rows that named pre-split test files now name the file where their title lives; titles are unchanged and the drift gate still resolves by title, so there is no behavioural change.
|
|
34
|
+
- Transport (#184, #190): the per-request deadline timer is now owned by one `try/finally` from the moment it is armed, so a cooldown or breaker refusal, or a throwing `logger.debug` / audit-body redaction, can no longer keep the event loop alive for `timeoutMs`; regression tests spy on `clearTimeout`.
|
|
35
|
+
- Types (#191): the 24 `BaseResource` members that went `private` -> `protected` only to span the query/meta/mutations layers (`cachedGet`, `walkBounded`, `latestWalk`, `unwrapCreateOperation`, ...) are marked `@internal` and stripped from the published `.d.ts` (`stripInternal`); they are not public API and subclasses outside the package cannot rely on them.
|
|
36
|
+
- Tooling (#187, #188, #189): the cache-write-invalidation static guard now scans every `boundedPart`/`readPart`/`windowedPart`/`expandByMonth` caller across all of `src/`; `derive-plan` finds BaseResource methods in `src/resources/base/{query,meta,mutations}.ts`; the test-title lookup ignores comments and `emit(`/`submit(` lookalikes, honours the injected `readFile`, and the capability gate fails a pinned title declared in more than one test file.
|
|
37
|
+
|
|
38
|
+
- Capability gate (#185): plan `tests[]` rows are now resolved by title across `test/**` (the `file` field is informational); `staleCheck.testFiles`/`testHash` are dropped from the registry. Plan hash unchanged. Test files can be renamed/split freely; the six sprint-coded test files were split by concern.
|
|
39
|
+
- Security (dependabot #182, #183): dev-scope lockfile bumps only — @modelcontextprotocol/server 2.2.0 → 2.3.0 (fixes GHSA-46jr-4wx8-v453, GHSA-jm63-cq47-j5jp, GHSA-rvq5-wwqv-78pq) and vite 8.3.1 → 8.3.4 (fixes GHSA-587h-7wmj-h5pm, GHSA-698v-4r3x-3vcv, GHSA-r825-w8mm-5pxw). No runtime surface change (the package has no runtime dependencies).
|
|
40
|
+
|
|
41
|
+
## [0.21.0] - 2026-10-21
|
|
42
|
+
|
|
43
|
+
**Structured bag properties, core ops in the catalog, op-key error text (issues #162, #163, #164, #177, #178, #179, #180, #181).** The plan hash moves to `baf3e855956e` (MINOR) — plan-pinned consumers must re-pin.
|
|
44
|
+
- **Bag `properties` (#162).** every object-typed `inputSchema` arg whose prose declares a `{ key?: type (...) }` bag now also carries `properties: [{ name, type, optional, default? }]` (e.g. `companies.query` params: `limit` default 500, `fieldCheck` default true), derived from the same description text at generation time so the two cannot drift. Additive; the prose is unchanged.
|
|
45
|
+
- **Core ops in `MCP_TOOL_CATALOG.catalog` (#163).** the 10 core-profile backing ops (`resources.whoAmI`, `version.get`, `threshold_information.get`, `operations.resolveAny`, `operations.searchAcrossResources`, `{companies,configuration_items,contacts,projects,tickets}.resolve`) are now catalog rows, flagged `core: true` (every row carries `core`); `counts.catalogRows` equals `registryOperations` (1213). `coreProfile` is unchanged.
|
|
46
|
+
- **Property types (#177).** bag `properties` types are now real type expressions (`(number | string)[]` for `ticketIds`, `string` for the date strings, `'find' | 'delete'` for `operations.recovery` action); a test fails on any unrecognised type token.
|
|
47
|
+
- **Literal defaults (#179).** string-literal enum defaults (`companies.unbilledWork` `source`/`rollup`, `contracts.profitability` `labourCostMethod`) and the `operations.fieldCatalog` `include` array default now appear in `properties[].default`; non-literal prose defaults (e.g. 'today') stay prose-only.
|
|
48
|
+
- **Array defaults and bag keys (#180, #181).** `CapabilityArgSchema.properties[].default` is now typed `number | boolean | string | string[]` (also `BagProperty` and `capabilities.schema.json`, `default` allows an array of strings), so `operations.fieldCatalog` `include` default `['fields']` validates; `names` / `maxNameReads` (default false / 20) are now documented for `tickets.fullContext`, `companies.fullProfile` and `configuration_items.profile` and appear in their `properties`. A test checks every record's properties against the schema and every key of a named `*Options` interface against the record bag. `testHash` / `capabilities.json` served text moved.
|
|
49
|
+
- **Served text (#178).** served capability descriptions, the plan and docs point at the op key `operations.resolveAny` instead of the tool name.
|
|
50
|
+
- **Error text (#164).** the pre-request refusals of `operations.fetchMany` / `operations.expandResourceNames` for non-numeric string ids name the op key `operations.resolveAny`, not the tool name.
|
|
51
|
+
|
|
10
52
|
## [0.20.0] - 2026-10-20
|
|
11
53
|
|
|
12
54
|
**Filter pre-validation, the latest-probe count cache, and the window/search/aggregate served-text corrections (issues #125, #127, #128, #129, #130, #131).** The plan hash moves to `f6a579e0b7a8` (MINOR) — plan-pinned consumers must re-pin.
|