@wilnertech/halopsa-mcp-server 1.4.0 → 1.5.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/README.md +1 -1
- package/dist/api/client.d.ts +12 -2
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +74 -9
- package/dist/api/client.js.map +1 -1
- package/dist/api/errors.d.ts +16 -3
- package/dist/api/errors.d.ts.map +1 -1
- package/dist/api/errors.js +106 -28
- package/dist/api/errors.js.map +1 -1
- package/dist/cache/memory-cache.d.ts +1 -0
- package/dist/cache/memory-cache.d.ts.map +1 -1
- package/dist/cache/memory-cache.js +1 -0
- package/dist/cache/memory-cache.js.map +1 -1
- package/dist/tools/budgets.d.ts.map +1 -1
- package/dist/tools/budgets.js +7 -4
- package/dist/tools/budgets.js.map +1 -1
- package/dist/tools/milestones.d.ts.map +1 -1
- package/dist/tools/milestones.js +12 -9
- package/dist/tools/milestones.js.map +1 -1
- package/dist/tools/registrations.d.ts +4 -3
- package/dist/tools/registrations.d.ts.map +1 -1
- package/dist/tools/registrations.js +17 -12
- package/dist/tools/registrations.js.map +1 -1
- package/dist/tools/ticket-actions.d.ts +46 -0
- package/dist/tools/ticket-actions.d.ts.map +1 -1
- package/dist/tools/ticket-actions.js +102 -1
- package/dist/tools/ticket-actions.js.map +1 -1
- package/dist/tools/ticket-reference-data.d.ts +42 -1
- package/dist/tools/ticket-reference-data.d.ts.map +1 -1
- package/dist/tools/ticket-reference-data.js +144 -8
- package/dist/tools/ticket-reference-data.js.map +1 -1
- package/dist/tools/tickets.d.ts +33 -0
- package/dist/tools/tickets.d.ts.map +1 -1
- package/dist/tools/tickets.js +141 -6
- package/dist/tools/tickets.js.map +1 -1
- package/dist/types/tickets.d.ts +68 -0
- package/dist/types/tickets.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* HaloPSA Ticket Reference Data tools.
|
|
3
3
|
*
|
|
4
4
|
* Mirrors the existing pattern in `src/tools/reference-data.ts` for asset
|
|
5
|
-
* reference data. All
|
|
5
|
+
* reference data. All six tools default to compact responses (token
|
|
6
6
|
* optimization) and use 1-hour cache TTLs because reference data on a
|
|
7
7
|
* Halo tenant changes infrequently — most reads should hit cache.
|
|
8
8
|
*
|
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
* standard `format_options` block so callers can choose their style:
|
|
11
11
|
* `{ full: true }` is the path-of-least-surprise for ad-hoc lookups,
|
|
12
12
|
* while `format_options` keeps parity with the rest of the MCP surface.
|
|
13
|
+
*
|
|
14
|
+
* v1.5.0 changes:
|
|
15
|
+
* - Bug 3 (P0): Added list_halopsa_ticket_outcomes + getOutcomeByName helper.
|
|
16
|
+
* - Bug 6 (P1): list_halopsa_ticket_priorities now exposes slaid and accepts
|
|
17
|
+
* optional tickettype_id for default-SLA scoping.
|
|
18
|
+
* - Bug 7 (P1): list_halopsa_ticket_categories replaced per-tickettype loop
|
|
19
|
+
* with a single GET /Category call; no more hang.
|
|
13
20
|
*/
|
|
14
21
|
import { z } from 'zod';
|
|
15
22
|
import { formatResponse } from '../utils/formatter.js';
|
|
@@ -34,15 +41,30 @@ const ListTicketStatusesArgsSchema = z.object({
|
|
|
34
41
|
format_options: FormatOptionsSchema,
|
|
35
42
|
});
|
|
36
43
|
const ListTicketPrioritiesArgsSchema = z.object({
|
|
44
|
+
tickettype_id: z.number().int().optional()
|
|
45
|
+
.describe('Optional. When supplied, fetches the ticket type and filters priorities to the default SLA ' +
|
|
46
|
+
'for that ticket type. Falls back to all 12 records if the ticket type has no default SLA. ' +
|
|
47
|
+
"Halo priorities are SLA-scoped — pass tickettype_id to narrow to the relevant SLA, or " +
|
|
48
|
+
'inspect slaid on each record yourself.'),
|
|
37
49
|
full: z.boolean().optional()
|
|
38
50
|
.describe('Return full records instead of compact (default false).'),
|
|
39
51
|
format_options: FormatOptionsSchema,
|
|
40
52
|
});
|
|
41
53
|
const ListTicketCategoriesArgsSchema = z.object({
|
|
54
|
+
tickettype_id: z.number().int().optional()
|
|
55
|
+
.describe('Optional. When supplied, filters categories to those whose type_id matches this ticket type ID ' +
|
|
56
|
+
'(client-side filter — Halo returns all categories regardless of server-side params).'),
|
|
42
57
|
full: z.boolean().optional()
|
|
43
58
|
.describe('Return full records instead of compact (default false).'),
|
|
44
59
|
format_options: FormatOptionsSchema,
|
|
45
60
|
});
|
|
61
|
+
const ListTicketOutcomesArgsSchema = z.object({
|
|
62
|
+
tickettype_id: z.number().int().optional()
|
|
63
|
+
.describe("Optional. When supplied, fetches the ticket type (with includedetails=true) and filters the " +
|
|
64
|
+
"global outcome list to the IDs listed in the tickettype's outcomes[] array. The Halo " +
|
|
65
|
+
'/Outcome endpoint does not support server-side tickettype filtering — scoping is client-side.'),
|
|
66
|
+
format_options: FormatOptionsSchema,
|
|
67
|
+
});
|
|
46
68
|
// =============================================================================
|
|
47
69
|
// Helpers
|
|
48
70
|
// =============================================================================
|
|
@@ -70,6 +92,7 @@ function resolveFormat(full, formatOptions) {
|
|
|
70
92
|
*
|
|
71
93
|
* We promote `priorityid` to `id` and expose the GUID as `sla_priority_id`
|
|
72
94
|
* to prevent callers from accidentally passing the GUID as priority_id.
|
|
95
|
+
* `slaid` is preserved so callers can disambiguate same-name priorities.
|
|
73
96
|
*/
|
|
74
97
|
function normalizePriority(p) {
|
|
75
98
|
return {
|
|
@@ -96,6 +119,32 @@ function unwrapList(response, preferredKey) {
|
|
|
96
119
|
const record_count = wrapped.record_count ?? items.length;
|
|
97
120
|
return { items, record_count };
|
|
98
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Compact an outcome record into the token-efficient shape.
|
|
124
|
+
*
|
|
125
|
+
* is_resolution heuristic: name contains "Resolved", "Closed", or "Completed"
|
|
126
|
+
* (case-insensitive), OR systemid is a non-zero value (closure outcomes in
|
|
127
|
+
* Halo carry a non-zero systemid).
|
|
128
|
+
*
|
|
129
|
+
* is_internal_note heuristic: respondtype === 2.
|
|
130
|
+
*
|
|
131
|
+
* NOTE: both flags are heuristics. Verify against your tenant's outcome
|
|
132
|
+
* configuration before relying on them for automation.
|
|
133
|
+
*/
|
|
134
|
+
function compactOutcome(o) {
|
|
135
|
+
const resolutionNames = /resolved|closed|completed/i;
|
|
136
|
+
const is_resolution = resolutionNames.test(o.outcome ?? '') || ((o.systemid ?? 0) > 0);
|
|
137
|
+
const is_internal_note = o.respondtype === 2;
|
|
138
|
+
return {
|
|
139
|
+
id: o.id,
|
|
140
|
+
name: o.outcome,
|
|
141
|
+
button: o.buttonname,
|
|
142
|
+
sequence: o.sequence,
|
|
143
|
+
hidden: o.hidden,
|
|
144
|
+
is_resolution,
|
|
145
|
+
is_internal_note,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
99
148
|
// =============================================================================
|
|
100
149
|
// Implementations
|
|
101
150
|
// =============================================================================
|
|
@@ -130,25 +179,106 @@ export async function listTicketStatuses(client, args) {
|
|
|
130
179
|
}
|
|
131
180
|
export async function listTicketPriorities(client, args) {
|
|
132
181
|
const formatOpts = resolveFormat(args.full, args.format_options);
|
|
182
|
+
// Fetch global priority list (12 records = 3 SLAs x 4 levels).
|
|
133
183
|
const response = await client.getCached('/Priority', { count: 500 }, {
|
|
134
184
|
enabled: true,
|
|
135
185
|
ttl: TTL.TICKET_PRIORITIES,
|
|
136
186
|
keyPrefix: 'ticketpriorities:all',
|
|
137
187
|
});
|
|
138
|
-
const { items: rawItems
|
|
139
|
-
|
|
140
|
-
|
|
188
|
+
const { items: rawItems } = unwrapList(response, 'priorities');
|
|
189
|
+
let normalized = rawItems.map(normalizePriority);
|
|
190
|
+
// Bug 6 fix: if tickettype_id supplied, scope to that type's default SLA.
|
|
191
|
+
if (args.tickettype_id !== undefined) {
|
|
192
|
+
const ttResponse = await client.getCached(`/TicketType/${args.tickettype_id}`, { includedetails: true }, {
|
|
193
|
+
enabled: true,
|
|
194
|
+
ttl: TTL.TICKET_TYPES,
|
|
195
|
+
keyPrefix: `tickettype:detail:${args.tickettype_id}`,
|
|
196
|
+
});
|
|
197
|
+
// Support both field names Halo uses across versions.
|
|
198
|
+
const defaultSla = ttResponse.default_sla ?? ttResponse.sla_id;
|
|
199
|
+
if (defaultSla !== undefined && defaultSla !== null) {
|
|
200
|
+
const filtered = normalized.filter((p) => p.slaid === defaultSla);
|
|
201
|
+
// Fall back to all records if the SLA filter yields nothing (e.g. tenant
|
|
202
|
+
// uses a different field name we have not mapped).
|
|
203
|
+
if (filtered.length > 0) {
|
|
204
|
+
normalized = filtered;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// If no default SLA on the tickettype, return all 12 (documented fallback).
|
|
208
|
+
}
|
|
209
|
+
return formatResponse(normalized, formatOpts, { record_count: normalized.length });
|
|
141
210
|
}
|
|
142
211
|
export async function listTicketCategories(client, args) {
|
|
143
212
|
const formatOpts = resolveFormat(args.full, args.format_options);
|
|
144
|
-
|
|
213
|
+
// Bug 7 fix: single GET /Category — no loop, no per-tickettype fetching.
|
|
214
|
+
// The Halo endpoint ignores count/tickettype_id params and always returns
|
|
215
|
+
// the full list (~90 records). Caching prevents repeat hits.
|
|
216
|
+
const response = await client.getCached('/Category', {}, {
|
|
145
217
|
enabled: true,
|
|
146
218
|
ttl: TTL.TICKET_CATEGORIES,
|
|
147
219
|
keyPrefix: 'ticketcategories:all',
|
|
148
220
|
});
|
|
149
|
-
|
|
221
|
+
let { items, record_count } = unwrapList(response, 'categories');
|
|
222
|
+
// Optional client-side filter by type_id (== tickettype_id in Halo's schema).
|
|
223
|
+
if (args.tickettype_id !== undefined) {
|
|
224
|
+
items = items.filter((c) => c.type_id === args.tickettype_id);
|
|
225
|
+
record_count = items.length;
|
|
226
|
+
}
|
|
150
227
|
return formatResponse(items, formatOpts, { record_count });
|
|
151
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* List all ticket outcomes from GET /Outcome (cached 1h).
|
|
231
|
+
*
|
|
232
|
+
* If tickettype_id is supplied, fetches the ticket type with includedetails=true
|
|
233
|
+
* and filters to the outcome IDs in its `outcomes[]` array.
|
|
234
|
+
*
|
|
235
|
+
* NOTE: The /Outcome endpoint does not support server-side tickettype filtering.
|
|
236
|
+
* Scoping is always performed client-side.
|
|
237
|
+
*/
|
|
238
|
+
export async function listTicketOutcomes(client, args) {
|
|
239
|
+
const formatOpts = args.format_options ?? { format: 'compact' };
|
|
240
|
+
// Fetch global outcome list.
|
|
241
|
+
const response = await client.getCached('/Outcome', { count: 200 }, {
|
|
242
|
+
enabled: true,
|
|
243
|
+
ttl: TTL.TICKET_OUTCOMES,
|
|
244
|
+
keyPrefix: 'ticketoutcomes:all',
|
|
245
|
+
});
|
|
246
|
+
let { items } = unwrapList(response, 'outcomes');
|
|
247
|
+
// Optional client-side filter via tickettype's outcomes[] array.
|
|
248
|
+
if (args.tickettype_id !== undefined) {
|
|
249
|
+
const ttResponse = await client.getCached(`/TicketType/${args.tickettype_id}`, { includedetails: true }, {
|
|
250
|
+
enabled: true,
|
|
251
|
+
ttl: TTL.TICKET_TYPES,
|
|
252
|
+
keyPrefix: `tickettype:detail:${args.tickettype_id}`,
|
|
253
|
+
});
|
|
254
|
+
const allowedIds = ttResponse.outcomes;
|
|
255
|
+
if (Array.isArray(allowedIds) && allowedIds.length > 0) {
|
|
256
|
+
const idSet = new Set(allowedIds);
|
|
257
|
+
items = items.filter((o) => idSet.has(o.id));
|
|
258
|
+
}
|
|
259
|
+
// If the tickettype has no outcomes[] array, return the full global list.
|
|
260
|
+
}
|
|
261
|
+
const compacted = items.map(compactOutcome);
|
|
262
|
+
return formatResponse(compacted, formatOpts, { record_count: compacted.length });
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Look up a single outcome by name (case-insensitive, first match).
|
|
266
|
+
*
|
|
267
|
+
* Returns null if no matching outcome is found. Used internally by ticket
|
|
268
|
+
* action tools that need to resolve an outcome name to its ID (e.g., SLA
|
|
269
|
+
* hold/release tools in ticket-actions.ts).
|
|
270
|
+
*/
|
|
271
|
+
export async function getOutcomeByName(client, name) {
|
|
272
|
+
const response = await client.getCached('/Outcome', { count: 200 }, {
|
|
273
|
+
enabled: true,
|
|
274
|
+
ttl: TTL.TICKET_OUTCOMES,
|
|
275
|
+
keyPrefix: 'ticketoutcomes:all',
|
|
276
|
+
});
|
|
277
|
+
const { items } = unwrapList(response, 'outcomes');
|
|
278
|
+
const lower = name.toLowerCase();
|
|
279
|
+
const match = items.find((o) => (o.outcome ?? '').toLowerCase() === lower);
|
|
280
|
+
return match ? compactOutcome(match) : null;
|
|
281
|
+
}
|
|
152
282
|
// =============================================================================
|
|
153
283
|
// Tool Definitions
|
|
154
284
|
// =============================================================================
|
|
@@ -172,14 +302,20 @@ export const listTicketStatusesTool = {
|
|
|
172
302
|
};
|
|
173
303
|
export const listTicketPrioritiesTool = {
|
|
174
304
|
name: 'list_halopsa_ticket_priorities',
|
|
175
|
-
description:
|
|
305
|
+
description: "List HaloPSA ticket priorities. Halo's priorities are SLA-scoped — each SLA has its own priority levels with overlapping integer ids (1-4). Pass `tickettype_id` to filter to that ticket type's default SLA, OR inspect `slaid` on each record to disambiguate. The `id` field is the integer priority value (1-4) that maps to `Faults.priority_id` on ticket creates — NOT the underlying GUID (exposed as `sla_priority_id`). Returns all 12 records (3 SLAs x 4 levels) when no tickettype_id is supplied. Includes fix/response time hints. Cached 1h.",
|
|
176
306
|
schema: ListTicketPrioritiesArgsSchema,
|
|
177
307
|
handler: listTicketPriorities,
|
|
178
308
|
};
|
|
179
309
|
export const listTicketCategoriesTool = {
|
|
180
310
|
name: 'list_halopsa_ticket_categories',
|
|
181
|
-
description: 'List all HaloPSA ticket categories (used for category_1..category_4 fields on tickets). Cached 1h.',
|
|
311
|
+
description: 'List all HaloPSA ticket categories (used for category_1..category_4 fields on tickets). Fetches GET /Category once and returns all categories (~90 records). Pass optional tickettype_id for client-side filtering by type_id. Cached 1h.',
|
|
182
312
|
schema: ListTicketCategoriesArgsSchema,
|
|
183
313
|
handler: listTicketCategories,
|
|
184
314
|
};
|
|
315
|
+
export const listTicketOutcomesTool = {
|
|
316
|
+
name: 'list_halopsa_ticket_outcomes',
|
|
317
|
+
description: "List HaloPSA ticket outcomes — the action outcomes selectable when adding a ticket action (e.g., Triage, Resolved, Closed, Escalated). Returns compact shape: {id, name, button, sequence, hidden, is_resolution, is_internal_note}. NOTE: is_resolution and is_internal_note are HEURISTICS (name-match + respondtype) — verify against your tenant's outcome configuration before automation. Pass optional tickettype_id to filter to outcomes allowed for that ticket type (client-side via the tickettype's outcomes[] array). GET /Outcome does not support server-side filtering. Cached 1h.",
|
|
318
|
+
schema: ListTicketOutcomesArgsSchema,
|
|
319
|
+
handler: listTicketOutcomes,
|
|
320
|
+
};
|
|
185
321
|
//# sourceMappingURL=ticket-reference-data.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ticket-reference-data.js","sourceRoot":"","sources":["../../src/tools/ticket-reference-data.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ticket-reference-data.js","sourceRoot":"","sources":["../../src/tools/ticket-reference-data.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/tickets.d.ts
CHANGED
|
@@ -98,6 +98,8 @@ export declare const CreateTicketArgsSchema: z.ZodObject<{
|
|
|
98
98
|
estimate: z.ZodOptional<z.ZodNumber>;
|
|
99
99
|
estimatedays: z.ZodOptional<z.ZodNumber>;
|
|
100
100
|
milestone_id: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
asset_id: z.ZodOptional<z.ZodNumber>;
|
|
102
|
+
asset_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
101
103
|
format_options: z.ZodOptional<z.ZodObject<{
|
|
102
104
|
format: z.ZodOptional<z.ZodEnum<{
|
|
103
105
|
compact: "compact";
|
|
@@ -132,6 +134,8 @@ export declare const UpdateTicketArgsSchema: z.ZodObject<{
|
|
|
132
134
|
estimate: z.ZodOptional<z.ZodNumber>;
|
|
133
135
|
estimatedays: z.ZodOptional<z.ZodNumber>;
|
|
134
136
|
milestone_id: z.ZodOptional<z.ZodNumber>;
|
|
137
|
+
asset_id: z.ZodOptional<z.ZodNumber>;
|
|
138
|
+
asset_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
135
139
|
format_options: z.ZodOptional<z.ZodObject<{
|
|
136
140
|
format: z.ZodOptional<z.ZodEnum<{
|
|
137
141
|
compact: "compact";
|
|
@@ -151,6 +155,19 @@ export declare const DeleteTicketArgsSchema: z.ZodObject<{
|
|
|
151
155
|
reason: z.ZodString;
|
|
152
156
|
confirm_destructive: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
153
157
|
}, z.core.$strip>;
|
|
158
|
+
export declare const ValidateCreateTicketArgsSchema: z.ZodObject<{
|
|
159
|
+
tickettype_id: z.ZodNumber;
|
|
160
|
+
fields: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
161
|
+
format_options: z.ZodOptional<z.ZodObject<{
|
|
162
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
163
|
+
compact: "compact";
|
|
164
|
+
standard: "standard";
|
|
165
|
+
detailed: "detailed";
|
|
166
|
+
}>>;
|
|
167
|
+
fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
168
|
+
omit_empty: z.ZodOptional<z.ZodBoolean>;
|
|
169
|
+
}, z.core.$strip>>;
|
|
170
|
+
}, z.core.$strip>;
|
|
154
171
|
export declare function listTickets(client: HaloPSAAPIClient, args: z.infer<typeof ListTicketsArgsSchema>): Promise<string>;
|
|
155
172
|
export declare function getTicket(client: HaloPSAAPIClient, args: z.infer<typeof GetTicketArgsSchema>): Promise<string>;
|
|
156
173
|
export declare function searchTickets(client: HaloPSAAPIClient, args: z.infer<typeof SearchTicketsArgsSchema>): Promise<string>;
|
|
@@ -182,6 +199,21 @@ export declare function updateTicket(client: HaloPSAAPIClient, args: z.infer<typ
|
|
|
182
199
|
*/
|
|
183
200
|
export declare function deleteTicket(client: HaloPSAAPIClient, args: z.infer<typeof DeleteTicketArgsSchema>): Promise<string>;
|
|
184
201
|
export declare function closeTicket(client: HaloPSAAPIClient, args: z.infer<typeof CloseTicketArgsSchema>): Promise<string>;
|
|
202
|
+
/**
|
|
203
|
+
* Pure-local validation of a proposed ticket payload against the cached
|
|
204
|
+
* tickettype schema. No write is performed — this is a dry-run helper.
|
|
205
|
+
*
|
|
206
|
+
* Required-field detection logic:
|
|
207
|
+
* Halo's TicketType details response includes a `fields[]` array where each
|
|
208
|
+
* entry has a `fieldinfo` sub-object. The `mandatory` flag on `fieldinfo`
|
|
209
|
+
* indicates a required field. Additionally, `technew` on `fieldinfo` where
|
|
210
|
+
* the value equals 3 indicates "required for tech/API create". When either
|
|
211
|
+
* condition is true, the field is counted as required.
|
|
212
|
+
*
|
|
213
|
+
* Standard ticket fields (summary, client_id, tickettype_id) are always
|
|
214
|
+
* required regardless of the tickettype schema.
|
|
215
|
+
*/
|
|
216
|
+
export declare function validateCreateTicket(client: HaloPSAAPIClient, args: z.infer<typeof ValidateCreateTicketArgsSchema>): Promise<string>;
|
|
185
217
|
export declare const listTicketsTool: ZodToolDefinition;
|
|
186
218
|
export declare const getTicketTool: ZodToolDefinition;
|
|
187
219
|
export declare const searchTicketsTool: ZodToolDefinition;
|
|
@@ -189,4 +221,5 @@ export declare const createTicketTool: ZodToolDefinition;
|
|
|
189
221
|
export declare const updateTicketTool: ZodToolDefinition;
|
|
190
222
|
export declare const closeTicketTool: ZodToolDefinition;
|
|
191
223
|
export declare const deleteTicketTool: ZodToolDefinition;
|
|
224
|
+
export declare const validateCreateTicketTool: ZodToolDefinition;
|
|
192
225
|
//# sourceMappingURL=tickets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tickets.d.ts","sourceRoot":"","sources":["../../src/tools/tickets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAgCvD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwChC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;iBAI9B,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;iBAUlC,CAAC;AAEH,eAAO,MAAM,sBAAsB
|
|
1
|
+
{"version":3,"file":"tickets.d.ts","sourceRoot":"","sources":["../../src/tools/tickets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAgCvD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwChC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;iBAI9B,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;iBAUlC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6CjC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmCjC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;iBAKhC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;iBAQjC,CAAC;AAGH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;iBAMzC,CAAC;AA0HH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GAC1C,OAAO,CAAC,MAAM,CAAC,CAwCjB;AAED,wBAAsB,SAAS,CAC7B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,GACxC,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,GAC5C,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,CAoDjB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,CA6GjB;AAgBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,CA8HjB;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GAC1C,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAiBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,GACnD,OAAO,CAAC,MAAM,CAAC,CAiGjB;AAMD,eAAO,MAAM,eAAe,EAAE,iBAK7B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,iBAK3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,iBAK/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,iBAK9B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,iBAK9B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,iBAK7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,iBAK9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,iBAMtC,CAAC"}
|
package/dist/tools/tickets.js
CHANGED
|
@@ -131,6 +131,11 @@ export const CreateTicketArgsSchema = z.object({
|
|
|
131
131
|
.describe('Estimated business days. Companion to estimate; pass either or neither — Halo auto-computes both from startdate/targetdate when both omitted.'),
|
|
132
132
|
milestone_id: z.number().int().optional()
|
|
133
133
|
.describe('Link this ticket to an existing milestone on its parent project ticket. milestone_id orchestrates: the MCP transparently translates this to a parent-project milestones[].tickets[] update. Single milestone per task — setting milestone_id automatically unlinks from any previous milestone. Pass 0 to unlink entirely. Find milestone ids via list_halopsa_ticket_milestones on the parent ticket.'),
|
|
134
|
+
// Bug 9: Asset linking fields.
|
|
135
|
+
asset_id: z.number().int().optional()
|
|
136
|
+
.describe('Single asset to link to this ticket. For multiple, use asset_ids.'),
|
|
137
|
+
asset_ids: z.array(z.number().int()).optional()
|
|
138
|
+
.describe('Multiple asset IDs to link to this ticket. Halo will populate the ticket.assets[] array. If both asset_id and asset_ids are set, they are combined.'),
|
|
134
139
|
format_options: FormatOptionsSchema,
|
|
135
140
|
});
|
|
136
141
|
export const UpdateTicketArgsSchema = z.object({
|
|
@@ -162,6 +167,11 @@ export const UpdateTicketArgsSchema = z.object({
|
|
|
162
167
|
.describe('Estimated business days. Companion to estimate; pass either or neither — Halo auto-computes both from startdate/targetdate when both omitted.'),
|
|
163
168
|
milestone_id: z.number().int().optional()
|
|
164
169
|
.describe('Link this ticket to an existing milestone on its parent project ticket. milestone_id orchestrates: the MCP transparently translates this to a parent-project milestones[].tickets[] update. Single milestone per task — setting milestone_id automatically unlinks from any previous milestone. Pass 0 to unlink entirely. Find milestone ids via list_halopsa_ticket_milestones on the parent ticket.'),
|
|
170
|
+
// Bug 9: Asset linking fields.
|
|
171
|
+
asset_id: z.number().int().optional()
|
|
172
|
+
.describe('Single asset to link to this ticket. For multiple, use asset_ids.'),
|
|
173
|
+
asset_ids: z.array(z.number().int()).optional()
|
|
174
|
+
.describe('Multiple asset IDs to link to this ticket. Halo will populate the ticket.assets[] array. If both asset_id and asset_ids are set, they are combined.'),
|
|
165
175
|
format_options: FormatOptionsSchema,
|
|
166
176
|
});
|
|
167
177
|
export const CloseTicketArgsSchema = z.object({
|
|
@@ -175,6 +185,14 @@ export const DeleteTicketArgsSchema = z.object({
|
|
|
175
185
|
reason: z.string().min(5).describe('REQUIRED audit-trail reason. To bypass safety blocks, the reason MUST include the substring "OVERRIDE" (case-sensitive) AND confirm_destructive must be true.'),
|
|
176
186
|
confirm_destructive: z.boolean().optional().default(false).describe('When safety pre-flight checks would block the delete, set true AND include "OVERRIDE" in `reason` to proceed. Default false.'),
|
|
177
187
|
});
|
|
188
|
+
// Bug 8: Schema for validate_halopsa_create_ticket.
|
|
189
|
+
export const ValidateCreateTicketArgsSchema = z.object({
|
|
190
|
+
tickettype_id: z.number().int()
|
|
191
|
+
.describe('Ticket type ID to validate against — see list_halopsa_tickettypes'),
|
|
192
|
+
fields: z.record(z.string(), z.unknown())
|
|
193
|
+
.describe('Proposed ticket payload as a key-value map. Keys should be field names (e.g. summary, client_id, customfields).'),
|
|
194
|
+
format_options: FormatOptionsSchema,
|
|
195
|
+
});
|
|
178
196
|
// =============================================================================
|
|
179
197
|
// Helpers
|
|
180
198
|
// =============================================================================
|
|
@@ -251,6 +269,24 @@ async function resolveClosedStatusId(client) {
|
|
|
251
269
|
`(received ${items.length} statuses; "Closed" matches: ${exactClosed.length}, "Completed" matches: ${exactCompleted.length}) ` +
|
|
252
270
|
'Pass status_id explicitly via update_halopsa_ticket.');
|
|
253
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Build the Halo assets[] array payload from asset_id / asset_ids args.
|
|
274
|
+
* Halo Faults.assets[] accepts elements with at minimum { id: number }.
|
|
275
|
+
* Returns undefined when neither arg is set (no-op).
|
|
276
|
+
*/
|
|
277
|
+
function buildAssetsPayload(asset_id, asset_ids) {
|
|
278
|
+
if (asset_id === undefined && asset_ids === undefined) {
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
const idSet = [];
|
|
282
|
+
if (asset_ids !== undefined) {
|
|
283
|
+
idSet.push(...asset_ids);
|
|
284
|
+
}
|
|
285
|
+
if (asset_id !== undefined && !idSet.includes(asset_id)) {
|
|
286
|
+
idSet.push(asset_id);
|
|
287
|
+
}
|
|
288
|
+
return idSet.map((id) => ({ id }));
|
|
289
|
+
}
|
|
254
290
|
// =============================================================================
|
|
255
291
|
// Implementations
|
|
256
292
|
// =============================================================================
|
|
@@ -364,14 +400,22 @@ export async function createTicket(client, args) {
|
|
|
364
400
|
// milestone_id is NOT sent to Halo as a direct field — writing it to the
|
|
365
401
|
// child record is a no-op for Project Task tickets (tickettype_id=20).
|
|
366
402
|
// Milestone membership is orchestrated via linkTicketToMilestone below.
|
|
403
|
+
// Bug 9: Asset linking. Build assets[] from asset_id / asset_ids args.
|
|
404
|
+
// Halo Faults.assets[] accepts elements with at minimum { id: number }.
|
|
405
|
+
const assetsPayload = buildAssetsPayload(args.asset_id, args.asset_ids);
|
|
406
|
+
if (assetsPayload !== undefined) {
|
|
407
|
+
payload.assets = assetsPayload;
|
|
408
|
+
}
|
|
367
409
|
const created = unwrapWriteResponse(await client.post('/Tickets', [payload]));
|
|
368
410
|
client.invalidateCache('tickets:*');
|
|
369
411
|
// Bug 1: Orchestrate milestone linkage via parent project if requested.
|
|
370
412
|
if (args.milestone_id !== undefined) {
|
|
371
413
|
await linkTicketToMilestone(client, created.id, args.milestone_id, args.parent_id);
|
|
372
414
|
}
|
|
373
|
-
|
|
374
|
-
|
|
415
|
+
// Bug 5: Pass full format_options so fields/omit_empty flow through to formatResponse.
|
|
416
|
+
const formatOpts = args.format_options ?? { format: 'compact' };
|
|
417
|
+
const format = formatOpts.format ?? 'compact';
|
|
418
|
+
return formatResponse(stripWriteResponse(created, format, 'ticket'), formatOpts);
|
|
375
419
|
}
|
|
376
420
|
export async function updateTicket(client, args) {
|
|
377
421
|
const partial = {};
|
|
@@ -412,6 +456,11 @@ export async function updateTicket(client, args) {
|
|
|
412
456
|
// milestone_id is NOT sent to Halo as a direct field — writing it to the
|
|
413
457
|
// child record is a no-op for Project Task tickets (tickettype_id=20).
|
|
414
458
|
// Milestone membership is orchestrated via linkTicketToMilestone below.
|
|
459
|
+
// Bug 9: Asset linking. Build assets[] from asset_id / asset_ids args.
|
|
460
|
+
const assetsPayload = buildAssetsPayload(args.asset_id, args.asset_ids);
|
|
461
|
+
if (assetsPayload !== undefined) {
|
|
462
|
+
partial.assets = assetsPayload;
|
|
463
|
+
}
|
|
415
464
|
// Determine which fields count for the "at least one" check. We include
|
|
416
465
|
// milestone_id in the check so that a milestone-only update is valid.
|
|
417
466
|
const fieldCount = Object.keys(partial).length + (args.milestone_id !== undefined ? 1 : 0);
|
|
@@ -462,8 +511,10 @@ export async function updateTicket(client, args) {
|
|
|
462
511
|
if (args.milestone_id !== undefined) {
|
|
463
512
|
await linkTicketToMilestone(client, args.ticket_id, args.milestone_id, args.parent_id);
|
|
464
513
|
}
|
|
465
|
-
|
|
466
|
-
|
|
514
|
+
// Bug 5: Pass full format_options so fields/omit_empty flow through to formatResponse.
|
|
515
|
+
const formatOpts = args.format_options ?? { format: 'compact' };
|
|
516
|
+
const format = formatOpts.format ?? 'compact';
|
|
517
|
+
return formatResponse(stripWriteResponse(response, format, 'ticket'), formatOpts);
|
|
467
518
|
}
|
|
468
519
|
/**
|
|
469
520
|
* Permanently delete a HaloPSA ticket by ID after running four safety
|
|
@@ -601,6 +652,84 @@ export async function closeTicket(client, args) {
|
|
|
601
652
|
status_id: closedStatusId,
|
|
602
653
|
});
|
|
603
654
|
}
|
|
655
|
+
/**
|
|
656
|
+
* Pure-local validation of a proposed ticket payload against the cached
|
|
657
|
+
* tickettype schema. No write is performed — this is a dry-run helper.
|
|
658
|
+
*
|
|
659
|
+
* Required-field detection logic:
|
|
660
|
+
* Halo's TicketType details response includes a `fields[]` array where each
|
|
661
|
+
* entry has a `fieldinfo` sub-object. The `mandatory` flag on `fieldinfo`
|
|
662
|
+
* indicates a required field. Additionally, `technew` on `fieldinfo` where
|
|
663
|
+
* the value equals 3 indicates "required for tech/API create". When either
|
|
664
|
+
* condition is true, the field is counted as required.
|
|
665
|
+
*
|
|
666
|
+
* Standard ticket fields (summary, client_id, tickettype_id) are always
|
|
667
|
+
* required regardless of the tickettype schema.
|
|
668
|
+
*/
|
|
669
|
+
export async function validateCreateTicket(client, args) {
|
|
670
|
+
// Fetch the tickettype with full field details.
|
|
671
|
+
const tickettype = await client.getCached(`/TicketType/${args.tickettype_id}`, { includedetails: true }, {
|
|
672
|
+
enabled: true,
|
|
673
|
+
ttl: TTL.TICKET_TYPES,
|
|
674
|
+
keyPrefix: `tickettype:${args.tickettype_id}:details`,
|
|
675
|
+
});
|
|
676
|
+
// Standard fields always required on create.
|
|
677
|
+
const STANDARD_REQUIRED = ['summary', 'client_id', 'tickettype_id'];
|
|
678
|
+
// Build the list of required fields from the tickettype schema.
|
|
679
|
+
const schemaFields = Array.isArray(tickettype.fields)
|
|
680
|
+
? tickettype.fields
|
|
681
|
+
: [];
|
|
682
|
+
const requiredFromSchema = [];
|
|
683
|
+
const allSchemaFieldNames = [];
|
|
684
|
+
for (const f of schemaFields) {
|
|
685
|
+
// Field name may be on fieldinfo or directly on the field entry.
|
|
686
|
+
const info = f.fieldinfo ?? f;
|
|
687
|
+
const name = info.name ?? '';
|
|
688
|
+
const label = info.label ?? name;
|
|
689
|
+
const isCustom = info.custom ?? false;
|
|
690
|
+
if (name) {
|
|
691
|
+
allSchemaFieldNames.push(name);
|
|
692
|
+
}
|
|
693
|
+
// A field is required if mandatory===true OR technew===3.
|
|
694
|
+
const isRequired = info.mandatory === true || info.technew === 3;
|
|
695
|
+
if (isRequired && name) {
|
|
696
|
+
requiredFromSchema.push({ name, label, custom: isCustom });
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
// Combine standard required fields with schema-derived required fields,
|
|
700
|
+
// deduplicating by name.
|
|
701
|
+
const allRequiredFields = [
|
|
702
|
+
...STANDARD_REQUIRED.map((n) => ({ name: n, label: n, custom: false })),
|
|
703
|
+
...requiredFromSchema.filter((f) => !STANDARD_REQUIRED.includes(f.name)),
|
|
704
|
+
];
|
|
705
|
+
const providedKeys = new Set(Object.keys(args.fields));
|
|
706
|
+
// Missing: required fields not in the provided payload.
|
|
707
|
+
const missingRequired = allRequiredFields
|
|
708
|
+
.filter((f) => !providedKeys.has(f.name))
|
|
709
|
+
.map((f) => f.name);
|
|
710
|
+
// Unknown: provided fields not in the standard set and not in the schema.
|
|
711
|
+
const knownFieldNames = new Set([
|
|
712
|
+
...STANDARD_REQUIRED,
|
|
713
|
+
...allSchemaFieldNames,
|
|
714
|
+
// Common optional standard fields that are always valid.
|
|
715
|
+
'details_html', 'category_1', 'priority_id', 'status_id', 'source',
|
|
716
|
+
'agent_id', 'team_id', 'site_id', 'user_id', 'parent_id',
|
|
717
|
+
'dateoccurred', 'startdate', 'customfields', 'estimate', 'estimatedays',
|
|
718
|
+
'milestone_id', 'asset_id', 'asset_ids', 'assets',
|
|
719
|
+
]);
|
|
720
|
+
const unknownFields = [...providedKeys].filter((k) => !knownFieldNames.has(k));
|
|
721
|
+
const valid = missingRequired.length === 0 && unknownFields.length === 0;
|
|
722
|
+
const result = {
|
|
723
|
+
valid,
|
|
724
|
+
missing_required: missingRequired,
|
|
725
|
+
unknown_fields: unknownFields,
|
|
726
|
+
required_fields: allRequiredFields,
|
|
727
|
+
tickettype_id: args.tickettype_id,
|
|
728
|
+
tickettype_name: tickettype.name ?? '',
|
|
729
|
+
};
|
|
730
|
+
const formatOpts = args.format_options ?? { format: 'standard' };
|
|
731
|
+
return formatResponse(result, formatOpts);
|
|
732
|
+
}
|
|
604
733
|
// =============================================================================
|
|
605
734
|
// Tool Definitions
|
|
606
735
|
// =============================================================================
|
|
@@ -624,13 +753,13 @@ export const searchTicketsTool = {
|
|
|
624
753
|
};
|
|
625
754
|
export const createTicketTool = {
|
|
626
755
|
name: 'create_halopsa_ticket',
|
|
627
|
-
description: 'Create a new HaloPSA ticket. Required: summary, tickettype_id, client_id. Body is sent as POST /Tickets with array wrapper [{ ... }] (HaloPSA convention). Invalidates ticket cache. Estimate auto-computed from startdate->targetdate (5x8 business hrs) if omitted; pass `estimate` to override.',
|
|
756
|
+
description: 'Create a new HaloPSA ticket. Required: summary, tickettype_id, client_id. Body is sent as POST /Tickets with array wrapper [{ ... }] (HaloPSA convention). Invalidates ticket cache. Estimate auto-computed from startdate->targetdate (5x8 business hrs) if omitted; pass `estimate` to override. Pass asset_id/asset_ids to link assets on creation.',
|
|
628
757
|
schema: CreateTicketArgsSchema,
|
|
629
758
|
handler: createTicket,
|
|
630
759
|
};
|
|
631
760
|
export const updateTicketTool = {
|
|
632
761
|
name: 'update_halopsa_ticket',
|
|
633
|
-
description: 'Update an existing HaloPSA ticket. Performs read-before-write merge (Halo POST is REPLACE not PATCH). Update by explicit id only — no force/dedup flags (tickets are append-only events). Invalidates ticket cache.',
|
|
762
|
+
description: 'Update an existing HaloPSA ticket. Performs read-before-write merge (Halo POST is REPLACE not PATCH). Update by explicit id only — no force/dedup flags (tickets are append-only events). Invalidates ticket cache. Pass asset_id/asset_ids to link assets on update.',
|
|
634
763
|
schema: UpdateTicketArgsSchema,
|
|
635
764
|
handler: updateTicket,
|
|
636
765
|
};
|
|
@@ -646,4 +775,10 @@ export const deleteTicketTool = {
|
|
|
646
775
|
schema: DeleteTicketArgsSchema,
|
|
647
776
|
handler: deleteTicket,
|
|
648
777
|
};
|
|
778
|
+
export const validateCreateTicketTool = {
|
|
779
|
+
name: 'validate_halopsa_create_ticket',
|
|
780
|
+
description: 'Pure local dry-run validation of a proposed ticket payload against the cached tickettype schema. No Halo write is performed. Returns {valid, missing_required[], unknown_fields[], required_fields[]} so callers can catch missing mandatory fields before calling create_halopsa_ticket. Fetches tickettype via GET /TicketType/{id}?includedetails=true (cached 1h). Use list_halopsa_tickettypes to look up tickettype_id values.',
|
|
781
|
+
schema: ValidateCreateTicketArgsSchema,
|
|
782
|
+
handler: validateCreateTicket,
|
|
783
|
+
};
|
|
649
784
|
//# sourceMappingURL=tickets.js.map
|