iit-mcp-autotask 0.33.0 → 0.34.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/.env.example +1 -1
- package/README.md +4 -4
- package/capabilities.json +19 -19
- package/dist/tools/catalog-data.js +1 -1
- package/dist/tools/server-version.d.ts +1 -1
- package/dist/tools/server-version.js +1 -1
- package/dist/tools/server.d.ts +1 -1
- package/dist/tools/surface.js +1 -1
- package/dist/tools/write/preview.js +23 -5
- package/package.json +1 -1
- package/server.json +1 -1
- package/skills/{crm-onboarding.md → crm-management.md} +2 -2
- package/skills/filter-semantics.md +4 -2
package/.env.example
CHANGED
|
@@ -68,7 +68,7 @@ AUTOTASK_PROFILE=readonly
|
|
|
68
68
|
# The kill file: while it exists every preview and apply is denied (default <state dir>/KILL).
|
|
69
69
|
# AUTOTASK_KILL_FILE=/var/lib/autotask-mcp/KILL
|
|
70
70
|
# Optional toolsets on top of core (core is always on): a comma list of service, records, finance,
|
|
71
|
-
# or all. An unknown name fails boot. Unset = core only (
|
|
71
|
+
# or all. An unknown name fails boot. Unset = core only (11 tools).
|
|
72
72
|
# AUTOTASK_TOOLSETS=service,records,finance
|
|
73
73
|
# IANA time zone of the people asking (their local "today"), default UTC; not the tenant's or
|
|
74
74
|
# the server's zone. Autotask stores UTC; the zone is reported by
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ client + SDK-owned TTL caches).
|
|
|
19
19
|
|
|
20
20
|
## Tool surface
|
|
21
21
|
|
|
22
|
-
The **core** toolset (
|
|
22
|
+
The **core** toolset (11 tools) is always served; `AUTOTASK_TOOLSETS` adds `service`, `records` and
|
|
23
23
|
`finance` (or `all`, 18 tools); the operator profile adds two. The whole tool surface (20 tools) is
|
|
24
24
|
listed below. Every other operation in the SDK's 1,216-operation registry stays
|
|
25
25
|
reachable through the generic layer: `autotask_catalog` finds the key, `autotask_describe` serves
|
|
@@ -27,8 +27,8 @@ its contract, `autotask_read` runs it.
|
|
|
27
27
|
|
|
28
28
|
| Toolset | Tools |
|
|
29
29
|
|---------|-------|
|
|
30
|
-
| core (always) | `autotask_guide` · `autotask_catalog` · `autotask_describe` · `autotask_read` · `autotask_resolve` · `autotask_query` · `autotask_fetch_many` · `autotask_tenant_status` · `autotask_ticket_full_context` · `autotask_company_full_profile` |
|
|
31
|
-
| `service` | `autotask_project_get_context` · `autotask_service_call_full_profile`
|
|
30
|
+
| core (always) | `autotask_guide` · `autotask_catalog` · `autotask_describe` · `autotask_read` · `autotask_resolve` · `autotask_query` · `autotask_fetch_many` · `autotask_tenant_status` · `autotask_ticket_full_context` · `autotask_company_full_profile` · `autotask_resource_workload` |
|
|
31
|
+
| `service` | `autotask_project_get_context` · `autotask_service_call_full_profile` |
|
|
32
32
|
| `records` | `autotask_contact_full_profile` · `autotask_configuration_item_profile` · `autotask_contract_full_profile` |
|
|
33
33
|
| `finance` | `autotask_quote_get` · `autotask_invoice_get` (`format`: `record`, `html`, `xml`, `pdf`) |
|
|
34
34
|
| `operator` (operator profile) | `autotask_write` (preview, changes nothing) · `autotask_apply` (execute the previewed plan) |
|
|
@@ -70,7 +70,7 @@ vendor, so loading a playbook costs no wire and adds no injection surface.
|
|
|
70
70
|
- `{skill, section}`: one of its `## ` sections.
|
|
71
71
|
|
|
72
72
|
Skills: `ticket-triage`, `company-360`, `unbilled-work-review`, `contract-profitability`,
|
|
73
|
-
`invoice-export`, `filter-semantics`, `slim-reads`, and on the operator profile `log-time-entry` and `crm-
|
|
73
|
+
`invoice-export`, `filter-semantics`, `slim-reads`, and on the operator profile `log-time-entry` and `crm-management`.
|
|
74
74
|
|
|
75
75
|
The playbooks are discoverable in both directions:
|
|
76
76
|
|
package/capabilities.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"domain": "autotask",
|
|
4
4
|
"description": "Machine-readable capability catalogue for iit-mcp-autotask: the tools the server can serve, with the toolset and the lowest profile that serves each. Everything else in the registry is reachable through the generic layer (autotask_catalog / autotask_describe / autotask_read) and documented in docs/capability-matrix.md. Generated from src/tools/surface.ts by scripts/build-capabilities.mjs.",
|
|
5
5
|
"exposedToolCount": {
|
|
6
|
-
"readonlyCore":
|
|
6
|
+
"readonlyCore": 11,
|
|
7
7
|
"readonlyAll": 18,
|
|
8
|
-
"operatorCore":
|
|
8
|
+
"operatorCore": 13,
|
|
9
9
|
"operatorAll": 20
|
|
10
10
|
},
|
|
11
11
|
"profiles": {
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"autotask_fetch_many",
|
|
28
28
|
"autotask_tenant_status",
|
|
29
29
|
"autotask_ticket_full_context",
|
|
30
|
-
"autotask_company_full_profile"
|
|
30
|
+
"autotask_company_full_profile",
|
|
31
|
+
"autotask_resource_workload"
|
|
31
32
|
],
|
|
32
33
|
"service": [
|
|
33
34
|
"autotask_project_get_context",
|
|
34
|
-
"autotask_service_call_full_profile"
|
|
35
|
-
"autotask_resource_workload"
|
|
35
|
+
"autotask_service_call_full_profile"
|
|
36
36
|
],
|
|
37
37
|
"records": [
|
|
38
38
|
"autotask_contact_full_profile",
|
|
@@ -211,14 +211,14 @@
|
|
|
211
211
|
}
|
|
212
212
|
},
|
|
213
213
|
{
|
|
214
|
-
"id": "autotask.
|
|
215
|
-
"description": "One
|
|
214
|
+
"id": "autotask.resource.workload",
|
|
215
|
+
"description": "One resource's availability: open tickets as primary or secondary resource (de-duplicated, with counts), service-call assignments, time-off balances and time-off awaiting approval. Missing parts degrade to typed absent.",
|
|
216
216
|
"mcpPrimitive": "tool",
|
|
217
|
-
"toolName": "
|
|
217
|
+
"toolName": "autotask_resource_workload",
|
|
218
218
|
"type": "read",
|
|
219
|
-
"toolset": "
|
|
219
|
+
"toolset": "core",
|
|
220
220
|
"profile": "readonly",
|
|
221
|
-
"backingOperation": "
|
|
221
|
+
"backingOperation": "resources.get",
|
|
222
222
|
"annotations": {
|
|
223
223
|
"readOnly": true,
|
|
224
224
|
"idempotent": true,
|
|
@@ -227,14 +227,14 @@
|
|
|
227
227
|
}
|
|
228
228
|
},
|
|
229
229
|
{
|
|
230
|
-
"id": "autotask.
|
|
231
|
-
"description": "One
|
|
230
|
+
"id": "autotask.project.get_context",
|
|
231
|
+
"description": "One project with its company, opportunity and lead resource. Project phases and task lists do not exist on this API; charges and notes go through autotask_read.",
|
|
232
232
|
"mcpPrimitive": "tool",
|
|
233
|
-
"toolName": "
|
|
233
|
+
"toolName": "autotask_project_get_context",
|
|
234
234
|
"type": "read",
|
|
235
235
|
"toolset": "service",
|
|
236
236
|
"profile": "readonly",
|
|
237
|
-
"backingOperation": "
|
|
237
|
+
"backingOperation": "projects.getContext",
|
|
238
238
|
"annotations": {
|
|
239
239
|
"readOnly": true,
|
|
240
240
|
"idempotent": true,
|
|
@@ -243,14 +243,14 @@
|
|
|
243
243
|
}
|
|
244
244
|
},
|
|
245
245
|
{
|
|
246
|
-
"id": "autotask.
|
|
247
|
-
"description": "One
|
|
246
|
+
"id": "autotask.service_call.full_profile",
|
|
247
|
+
"description": "One service call with its linked tickets, tasks and resource assignments, for on-site visit work. Missing parts degrade to typed absent.",
|
|
248
248
|
"mcpPrimitive": "tool",
|
|
249
|
-
"toolName": "
|
|
249
|
+
"toolName": "autotask_service_call_full_profile",
|
|
250
250
|
"type": "read",
|
|
251
251
|
"toolset": "service",
|
|
252
252
|
"profile": "readonly",
|
|
253
|
-
"backingOperation": "
|
|
253
|
+
"backingOperation": "service_calls.get",
|
|
254
254
|
"annotations": {
|
|
255
255
|
"readOnly": true,
|
|
256
256
|
"idempotent": true,
|
|
@@ -260,7 +260,7 @@
|
|
|
260
260
|
},
|
|
261
261
|
{
|
|
262
262
|
"id": "autotask.contact.full_profile",
|
|
263
|
-
"description": "One contact with their company and open tickets (closed set from the tenant status picklist, 2-
|
|
263
|
+
"description": "One contact with their company and open tickets (closed set from the tenant status picklist, 2-5 reads). Contains personal data. Missing parts degrade to typed absent.",
|
|
264
264
|
"mcpPrimitive": "tool",
|
|
265
265
|
"toolName": "autotask_contact_full_profile",
|
|
266
266
|
"type": "read",
|
|
@@ -121,7 +121,7 @@ export const TOOL_DESCRIPTIONS = {
|
|
|
121
121
|
autotask_project_get_context: 'One project with its company, opportunity and lead resource. Project phases and task lists do not exist on this API; charges and notes go through autotask_read.',
|
|
122
122
|
autotask_service_call_full_profile: 'One service call with its linked tickets, tasks and resource assignments, for on-site visit work. Missing parts degrade to typed absent.',
|
|
123
123
|
autotask_resource_workload: 'One resource\'s availability: open tickets as primary or secondary resource (de-duplicated, with counts), service-call assignments, time-off balances and time-off awaiting approval. Missing parts degrade to typed absent.',
|
|
124
|
-
autotask_contact_full_profile: 'One contact with their company and open tickets (closed set from the tenant status picklist, 2-
|
|
124
|
+
autotask_contact_full_profile: 'One contact with their company and open tickets (closed set from the tenant status picklist, 2-5 reads). Contains personal data. Missing parts degrade to typed absent.',
|
|
125
125
|
autotask_configuration_item_profile: 'One configuration item with type, category, notes, related items and billing products. Zone ww29 has no configuration items: expect not found there.',
|
|
126
126
|
autotask_contract_full_profile: 'One contract with services, billing rules, the 50 newest charges and service units. Monthly P&L: autotask_read (guide: contract-profitability).',
|
|
127
127
|
autotask_quote_get: 'One quote with its line items in one call. A list of quotes: autotask_read quotes.query.',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** The wire identity of this server: a leaf module (no imports) so any tool can read it without an import cycle. */
|
|
2
2
|
export const SERVER_NAME = 'iit-mcp-autotask';
|
|
3
|
-
export const SERVER_VERSION = '0.
|
|
3
|
+
export const SERVER_VERSION = '0.34.0';
|
|
4
4
|
//# sourceMappingURL=server-version.js.map
|
package/dist/tools/server.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export declare function serverInstructions(config: Pick<Config, 'AUTOTASK_PROFIL
|
|
|
60
60
|
/** The full identity stamp (the `/metadata` build block — the era pair, FR-2). */
|
|
61
61
|
export declare const SERVER_IDENTITY: {
|
|
62
62
|
readonly name: "iit-mcp-autotask";
|
|
63
|
-
readonly version: "0.
|
|
63
|
+
readonly version: "0.34.0";
|
|
64
64
|
readonly mcpProtocolBaseline: "2026-07-28";
|
|
65
65
|
readonly legacyProtocolFallback: "2025-11-25";
|
|
66
66
|
};
|
package/dist/tools/surface.js
CHANGED
|
@@ -25,9 +25,9 @@ export const SURFACE = [
|
|
|
25
25
|
{ name: 'autotask_tenant_status', toolset: 'core', profile: 'readonly', vendor: true, build: buildTenantStatus },
|
|
26
26
|
{ name: 'autotask_ticket_full_context', toolset: 'core', profile: 'readonly', vendor: true, build: buildTicketFullContext },
|
|
27
27
|
{ name: 'autotask_company_full_profile', toolset: 'core', profile: 'readonly', vendor: true, build: buildCompanyFullProfile },
|
|
28
|
+
{ name: 'autotask_resource_workload', toolset: 'core', profile: 'readonly', vendor: true, build: buildResourceWorkload },
|
|
28
29
|
{ name: 'autotask_project_get_context', toolset: 'service', profile: 'readonly', vendor: true, build: buildProjectGetContext },
|
|
29
30
|
{ name: 'autotask_service_call_full_profile', toolset: 'service', profile: 'readonly', vendor: true, build: buildServiceCallFullProfile },
|
|
30
|
-
{ name: 'autotask_resource_workload', toolset: 'service', profile: 'readonly', vendor: true, build: buildResourceWorkload },
|
|
31
31
|
{ name: 'autotask_contact_full_profile', toolset: 'records', profile: 'readonly', vendor: true, build: buildContactFullProfile },
|
|
32
32
|
{ name: 'autotask_configuration_item_profile', toolset: 'records', profile: 'readonly', vendor: true, build: buildConfigurationItemProfile },
|
|
33
33
|
{ name: 'autotask_contract_full_profile', toolset: 'records', profile: 'readonly', vendor: true, build: buildContractFullProfile },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** `autotask_write`: the preview tool (issue #89). */
|
|
2
2
|
import { MoveRefusedError } from 'node-autotask';
|
|
3
3
|
import { abortedResult, cleanUntrustedText } from '../results.js';
|
|
4
|
+
import { assertOperationPermitted } from '../../policy.js';
|
|
4
5
|
import { WRITE_INPUT, closedInput } from '../schemas.js';
|
|
5
6
|
import { hashOf } from '../../write/canonical.js';
|
|
6
7
|
import { evaluate } from '../../write/evaluate.js';
|
|
@@ -141,9 +142,13 @@ async function entryResource(r, a) {
|
|
|
141
142
|
return { id, roleID, name: null, absent: true };
|
|
142
143
|
}
|
|
143
144
|
}
|
|
145
|
+
/** The response sanitiser's per-string cap (UNTRUSTED_TEXT_CAP): a longer warning would lose its tail. */
|
|
146
|
+
const LOCATION_WARNING_CAP = 500;
|
|
144
147
|
/**
|
|
145
148
|
* #183: a contact written with no `companyLocationID` while its company has active locations. One bounded
|
|
146
|
-
* read (limit 50), only when the field is unset; a failed read adds nothing.
|
|
149
|
+
* read (limit 50), only when the field is unset; a failed or refused read adds nothing. The read goes through
|
|
150
|
+
* the same deployment gate as `autotask_read`. The sanitiser caps each warning at 500 characters, so the instruction
|
|
151
|
+
* comes first and only as many location ids as fit are listed. No vendor text enters the sentence: ids are numbers.
|
|
147
152
|
*/
|
|
148
153
|
async function locationWarning(r, a, current) {
|
|
149
154
|
if (r.op !== 'contacts.create' && r.op !== 'contacts.update')
|
|
@@ -154,12 +159,25 @@ async function locationWarning(r, a, current) {
|
|
|
154
159
|
if (current?.companyLocationID != null)
|
|
155
160
|
return undefined; // update on a contact that already has a location
|
|
156
161
|
try {
|
|
162
|
+
assertOperationPermitted('company_locations.query', r.ctx.record('company_locations.query'), r.ctx.config, r.ctx.log);
|
|
157
163
|
const rows = (await r.ctx.ops.invokeRead(r.ctx.client, 'company_locations.query', [{ filter: [{ field: 'companyID', op: 'eq', value: companyID }, { field: 'isActive', op: 'eq', value: true }], limit: 50 }], undefined, r.signal));
|
|
158
|
-
const
|
|
164
|
+
const raw = (Array.isArray(rows) ? rows : rows?.items) ?? [];
|
|
165
|
+
const list = raw.filter((l) => Number.isSafeInteger(l.id) && l.id > 0); // ids are the only thing served: no row without one is listed or counted
|
|
159
166
|
if (list.length === 0)
|
|
160
167
|
return undefined;
|
|
161
|
-
const
|
|
162
|
-
|
|
168
|
+
const total = list.length >= 50 ? 'at least 50' : String(list.length);
|
|
169
|
+
const head = `companyLocationID is not set on this contact and the parent company ${companyID} has ${total} active location(s). Set companyLocationID before applying; if the contact's address is unknown${list.length > 1 ? ', ask the user which location applies' : ' and there is one location, set it'}. Location ids (names: company_locations.query): `;
|
|
170
|
+
// ids and the primary flag only: no vendor text in server prose; the model reads names through company_locations.query
|
|
171
|
+
const items = list.map((l) => `${l.id}${l.isPrimary === true ? ' (primary)' : ''}`);
|
|
172
|
+
let text = head;
|
|
173
|
+
let shown = 0;
|
|
174
|
+
for (const it of items) {
|
|
175
|
+
if (text.length + it.length + 2 > LOCATION_WARNING_CAP - 40)
|
|
176
|
+
break; // 40 = room for the ' (first N of at least M listed)' tail
|
|
177
|
+
text += (shown > 0 ? ', ' : '') + it;
|
|
178
|
+
shown++;
|
|
179
|
+
}
|
|
180
|
+
return shown < items.length ? `${text} (first ${shown} of ${total} listed)` : text;
|
|
163
181
|
}
|
|
164
182
|
catch {
|
|
165
183
|
return undefined;
|
|
@@ -255,7 +273,7 @@ export function buildWrite(ctx) {
|
|
|
255
273
|
if ('stop' in dry)
|
|
256
274
|
return dry.stop;
|
|
257
275
|
const loc = await locationWarning(r, admitted.ok, snap.ok.current);
|
|
258
|
-
const dried = loc === undefined ? dry.ok : { ...dry.ok, warnings: [...(dry.ok.warnings ?? [])
|
|
276
|
+
const dried = loc === undefined ? dry.ok : { ...dry.ok, warnings: [loc, ...(dry.ok.warnings ?? [])] };
|
|
259
277
|
return persist(r, admitted.ok, snap.ok, evaluatePhase(r, admitted.ok, snap.ok, dried, await entryResource(r, admitted.ok)));
|
|
260
278
|
},
|
|
261
279
|
};
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: crm-
|
|
2
|
+
name: crm-management
|
|
3
3
|
kind: write
|
|
4
4
|
version: 1
|
|
5
5
|
profile: operator
|
|
@@ -10,7 +10,7 @@ inputs: {"type": "object", "required": ["intent", "run_id"], "properties": {"int
|
|
|
10
10
|
outcomes: ["ok", "needs_input", "ambiguous", "denied", "error"]
|
|
11
11
|
hazards: ["writes company, location and contact records to the PSA", "every new company gets an auto-created \"Primary Address\" location that cannot be deleted through the API: rename it", "companies, resources and countries are blind surfaces on some tenants: no match is not proof of absence; owners and countries are lookups, not lists"]
|
|
12
12
|
related: ["company-360"]
|
|
13
|
-
eval: evals/crm-
|
|
13
|
+
eval: evals/crm-management.json
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
# Create or update a company, location or contact
|
|
@@ -5,7 +5,7 @@ version: 1
|
|
|
5
5
|
profile: readonly
|
|
6
6
|
description: Filter footguns for autotask_query: strict date lt, case-insensitive contains, FK 0 vs NULL, field-name checks, all-strings values.
|
|
7
7
|
when_not_to_use: the filter grammar itself (the 13 operators + and/or grouping, server instructions); the per-operation notes (autotask_describe)
|
|
8
|
-
ops: ["autotask_query:tickets.query", "autotask_query:time_entries.query", "autotask_query:service_calls.query", "autotask_query:contracts.query", "autotask_query:time_off_requests.query", "autotask_read:operations.fieldCatalog"]
|
|
8
|
+
ops: ["autotask_query:tickets.query", "autotask_query:time_entries.query", "autotask_query:service_calls.query", "autotask_query:contracts.query", "autotask_query:time_off_requests.query", "autotask_read:operations.fieldCatalog", "autotask_resource_workload"]
|
|
9
9
|
inputs: {"type": "object", "required": [], "properties": {}}
|
|
10
10
|
outcomes: ["ok", "error"]
|
|
11
11
|
hazards: ["gt/lt around a day boundary silently drops an edge day", "ticketID eq 0 finds nothing (time_entries store NULL)", "an unknown field name is a zero-query-wire CONFIG_ERROR (0.20.0 pre-validation; a cold field cache makes one vendor metadata GET first)"]
|
|
@@ -29,10 +29,11 @@ No inputs: this is a reference page about the filter behaviour of autotask_query
|
|
|
29
29
|
4. Unassigned FKs are not uniform. tickets store 0; time_entries store NULL. Unallocated time needs notExist (ticketID eq 0 finds nothing); assignedResourceID eq 0 and notExist differ.
|
|
30
30
|
5. Field names are vendor casing, and an unknown field name now fails pre-dispatch as a typed CONFIG_ERROR with the field-catalog pointer, with zero query wire (0.20.0 pre-validation), on all five autotask_query operations (tickets.query, time_entries.query, service_calls.query, contracts.query, time_off_requests.query). One udf: true condition per query is exempt from the name check. List names for the queried resource with the scoped call `autotask_read {operation:"operations.fieldCatalog", args:[{resources:["<resource>"]}]}` (add `fields: [names]` to project; a scoped call also returns picklist labels by default on this server). Never the bare `operations.fieldCatalog`: it serves the whole five-resource catalog (~85 KB) and has no row for time_entries, service_calls, contracts or time_off_requests. Do not guess names. The answer is `data.results[0].result`.
|
|
31
31
|
6. Some clients (Copilot Studio) fail locally, before any request, when one array mixes value types (a number beside a string in one filter, or dateWorkedWindow args: strings plus an object). The autotask_query filter declares value as a string, so write every value as a string ("<resource id>", "2026-10-22", "true"); numbers still work for clients that send them. For a time entry window over a resource or ticket, query time entries instead of dateWorkedWindow: `autotask_query {resource:"time_entries", filter:[{field:"resourceID",op:"eq",value:"<resource id>"},{field:"dateWorked",op:"gte",value:"2026-10-20"},{field:"dateWorked",op:"lte",value:"2026-10-22"}], limit:500}`: the window's rows up to limit (dateWorkedWindow caps at 500; the query defaults to 50), both days inclusive because dateWorked is a date, without the posted counts (derive them from postedDate where the zone serves it). A bare window with no narrowing is `autotask_read {operation:"time_entries.dateWorkedWindow", args:["2026-10-20","2026-10-22"]}` (all strings). The server and SDK pass filter values through unchanged; string dates, ids and booleans are confirmed on resources and time_entries, other fields are not verified.
|
|
32
|
+
7. tickets.assignedResourceID is the PRIMARY owner only. Secondary assignments live in the separate ticket_secondary_resources table, which carries no status, so "how many open tickets does <resource> have" cannot be answered from tickets.query alone or from that table alone. Use `autotask_resource_workload {id}`: its open_tickets part is the de-duplicated union of primary and secondary open tickets with counts {primary, secondary, both, open}. The counts describe the returned set; when value.capped is true the answer is partial: call again with a larger ticket_limit (up to 500) before quoting counts, and say so if it is still capped. If the part is absent with CLOSED_STATUS_UNRESOLVED the tenant's closed statuses could not be read: say the count is unavailable rather than guessing.
|
|
32
33
|
|
|
33
34
|
## Guardrails
|
|
34
35
|
|
|
35
|
-
- Apply the
|
|
36
|
+
- Apply the seven rules above when composing or debugging any autotask_query filter.
|
|
36
37
|
- Do not "fix" a filter that already returns the right rows into a stricter spelling (a case-sensitive contains, an exclusive range, a zero-FK predicate).
|
|
37
38
|
- Never guess field names: since 0.20.0 an unknown name is a typed CONFIG_ERROR with zero query wire (a cold field cache makes one vendor metadata GET first) — list names with the scoped fieldCatalog call from step 5 first.
|
|
38
39
|
|
|
@@ -68,4 +69,5 @@ No inputs: this is a reference page about the filter behaviour of autotask_query
|
|
|
68
69
|
| autotask_query | contracts.query | read | - |
|
|
69
70
|
| autotask_query | time_off_requests.query | read | - |
|
|
70
71
|
| autotask_read | operations.fieldCatalog | read | - |
|
|
72
|
+
| autotask_resource_workload | - | - | - |
|
|
71
73
|
<!-- generated:end -->
|