@selfagency/teamdynamix-mcp 0.1.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/index.js ADDED
@@ -0,0 +1,1868 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+
7
+ // src/config.ts
8
+ import { z } from "zod";
9
+
10
+ // src/constants.ts
11
+ var SERVER_NAME = "teamdynamix-mcp";
12
+ var SERVER_VERSION = "0.2.0";
13
+ var TEAMDYNAMIX_TOOL_PREFIX = "teamdynamix";
14
+ var TEAMDYNAMIX_DEFAULT_TIMEOUT_MS = 3e4;
15
+ var TEAMDYNAMIX_DEFAULT_MAX_RETRIES = 2;
16
+ var TEAMDYNAMIX_MAX_RETRY_ATTEMPTS = 5;
17
+ var TEAMDYNAMIX_MIN_RATE_LIMIT_WAIT_MS = 5e3;
18
+ var TEAMDYNAMIX_MAX_RATE_LIMIT_WAIT_MS = 3e4;
19
+
20
+ // src/config.ts
21
+ var SERVER_NAME_OVERRIDE = process.env["MCP_SERVER_NAME"]?.trim() || void 0;
22
+ var SERVER_VERSION_OVERRIDE = process.env["MCP_SERVER_VERSION"]?.trim() || void 0;
23
+ var LOG_LEVEL = process.env["MCP_LOG_LEVEL"] === "debug" || process.env["MCP_LOG_LEVEL"] === "warn" || process.env["MCP_LOG_LEVEL"] === "error" ? process.env["MCP_LOG_LEVEL"] : "info";
24
+ function normalizeOptionalString(value) {
25
+ const normalized = value?.trim();
26
+ return normalized ? normalized : void 0;
27
+ }
28
+ function normalizeOptionalNumber(value) {
29
+ if (!value?.trim()) {
30
+ return void 0;
31
+ }
32
+ const parsed = Number.parseInt(value, 10);
33
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
34
+ }
35
+ function normalizeBoolean(value, defaultValue) {
36
+ if (!value) {
37
+ return defaultValue;
38
+ }
39
+ const normalized = value.trim().toLowerCase();
40
+ if (normalized === "true") return true;
41
+ if (normalized === "false") return false;
42
+ return defaultValue;
43
+ }
44
+ function normalizeNumberWithDefault(value, defaultValue, minimum) {
45
+ if (!value?.trim()) {
46
+ return defaultValue;
47
+ }
48
+ const parsed = Number.parseInt(value, 10);
49
+ return Number.isFinite(parsed) && parsed >= minimum ? parsed : defaultValue;
50
+ }
51
+ function normalizeTeamDynamixBaseUrl(value) {
52
+ const normalized = normalizeOptionalString(value);
53
+ return normalized ? normalized.replace(/\/+$/, "") : void 0;
54
+ }
55
+ function validateHttpsBaseUrl(value) {
56
+ if (!value) return void 0;
57
+ const schema = z.string().url().refine((url) => url.startsWith("https://"), {
58
+ message: "TEAMDYNAMIX_BASE_URL must be an https URL."
59
+ });
60
+ const parsed = schema.safeParse(value);
61
+ if (!parsed.success) {
62
+ throw new Error(parsed.error.issues[0]?.message ?? "Invalid TEAMDYNAMIX_BASE_URL.");
63
+ }
64
+ return value;
65
+ }
66
+ function normalizeTeamDynamixAuthMode(value) {
67
+ return value?.trim().toLowerCase() === "admin" ? "admin" : "standard";
68
+ }
69
+ function getTeamDynamixConfig() {
70
+ const baseUrl = validateHttpsBaseUrl(normalizeTeamDynamixBaseUrl(process.env["TEAMDYNAMIX_BASE_URL"]));
71
+ const maxRetries = normalizeNumberWithDefault(
72
+ process.env["TEAMDYNAMIX_MAX_RETRIES"],
73
+ TEAMDYNAMIX_DEFAULT_MAX_RETRIES,
74
+ 0
75
+ );
76
+ if (maxRetries > 5) {
77
+ throw new Error(`TEAMDYNAMIX_MAX_RETRIES must be between 0 and ${TEAMDYNAMIX_MAX_RETRY_ATTEMPTS}.`);
78
+ }
79
+ return {
80
+ baseUrl,
81
+ authMode: normalizeTeamDynamixAuthMode(process.env["TEAMDYNAMIX_AUTH_MODE"]),
82
+ username: normalizeOptionalString(process.env["TEAMDYNAMIX_USERNAME"]),
83
+ password: normalizeOptionalString(process.env["TEAMDYNAMIX_PASSWORD"]),
84
+ beid: normalizeOptionalString(process.env["TEAMDYNAMIX_BEID"]),
85
+ webServicesKey: normalizeOptionalString(process.env["TEAMDYNAMIX_WEB_SERVICES_KEY"]),
86
+ defaultTicketAppId: normalizeOptionalNumber(process.env["TEAMDYNAMIX_DEFAULT_TICKET_APP_ID"]),
87
+ defaultAssetAppId: normalizeOptionalNumber(process.env["TEAMDYNAMIX_DEFAULT_ASSET_APP_ID"]),
88
+ defaultKnowledgeBaseAppId: normalizeOptionalNumber(process.env["TEAMDYNAMIX_DEFAULT_KB_APP_ID"]),
89
+ timeoutMs: normalizeNumberWithDefault(process.env["TEAMDYNAMIX_TIMEOUT_MS"], TEAMDYNAMIX_DEFAULT_TIMEOUT_MS, 1e3),
90
+ maxRetries,
91
+ enableWriteTools: normalizeBoolean(process.env["TEAMDYNAMIX_ENABLE_WRITE_TOOLS"], false),
92
+ enableAdminTools: normalizeBoolean(process.env["TEAMDYNAMIX_ENABLE_ADMIN_TOOLS"], false)
93
+ };
94
+ }
95
+ function getTeamDynamixConfigStatus(config = getTeamDynamixConfig()) {
96
+ const missing = /* @__PURE__ */ new Set();
97
+ if (!config.baseUrl) {
98
+ missing.add("TEAMDYNAMIX_BASE_URL");
99
+ }
100
+ if (config.authMode === "admin") {
101
+ if (!config.beid) missing.add("TEAMDYNAMIX_BEID");
102
+ if (!config.webServicesKey) missing.add("TEAMDYNAMIX_WEB_SERVICES_KEY");
103
+ } else {
104
+ if (!config.username) missing.add("TEAMDYNAMIX_USERNAME");
105
+ if (!config.password) missing.add("TEAMDYNAMIX_PASSWORD");
106
+ }
107
+ return {
108
+ configured: missing.size === 0,
109
+ missing: [...missing],
110
+ authMode: config.authMode
111
+ };
112
+ }
113
+
114
+ // src/resources/teamdynamix.resources.ts
115
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
116
+
117
+ // src/services/teamdynamix/core.service.ts
118
+ function parseRateLimit(headers, nowMs = Date.now()) {
119
+ const limitRaw = headers.get("X-RateLimit-Limit");
120
+ const remainingRaw = headers.get("X-RateLimit-Remaining");
121
+ const resetRaw = headers.get("X-RateLimit-Reset");
122
+ const resetTime = resetRaw ? Date.parse(resetRaw) : Number.NaN;
123
+ const computedWaitMs = Number.isFinite(resetTime) ? resetTime - nowMs : TEAMDYNAMIX_MIN_RATE_LIMIT_WAIT_MS;
124
+ const waitMs = Math.min(
125
+ Math.max(
126
+ Number.isFinite(computedWaitMs) ? computedWaitMs : TEAMDYNAMIX_MIN_RATE_LIMIT_WAIT_MS,
127
+ TEAMDYNAMIX_MIN_RATE_LIMIT_WAIT_MS
128
+ ),
129
+ TEAMDYNAMIX_MAX_RATE_LIMIT_WAIT_MS
130
+ );
131
+ return {
132
+ limit: limitRaw ? Number.parseInt(limitRaw, 10) : null,
133
+ remaining: remainingRaw ? Number.parseInt(remainingRaw, 10) : null,
134
+ resetAt: Number.isFinite(resetTime) ? new Date(resetTime).toISOString() : null,
135
+ waitMs
136
+ };
137
+ }
138
+ function redactTeamDynamixConfig(config) {
139
+ return {
140
+ baseUrl: config.baseUrl ?? null,
141
+ authMode: config.authMode,
142
+ username: config.username ? "[configured]" : null,
143
+ password: config.password ? "[configured]" : null,
144
+ beid: config.beid ? "[configured]" : null,
145
+ webServicesKey: config.webServicesKey ? "[configured]" : null,
146
+ defaultTicketAppId: config.defaultTicketAppId ?? null,
147
+ defaultAssetAppId: config.defaultAssetAppId ?? null,
148
+ defaultKnowledgeBaseAppId: config.defaultKnowledgeBaseAppId ?? null,
149
+ timeoutMs: config.timeoutMs,
150
+ maxRetries: config.maxRetries,
151
+ enableWriteTools: config.enableWriteTools,
152
+ enableAdminTools: config.enableAdminTools
153
+ };
154
+ }
155
+ function decodeJwtExpiryEpochSeconds(token) {
156
+ const segments = token.split(".");
157
+ const payload = segments[1];
158
+ if (!payload) {
159
+ return null;
160
+ }
161
+ try {
162
+ const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
163
+ const padding = normalized.length % 4 === 0 ? "" : "=".repeat(4 - normalized.length % 4);
164
+ const decoded = Buffer.from(`${normalized}${padding}`, "base64").toString("utf8");
165
+ const parsed = JSON.parse(decoded);
166
+ return typeof parsed.exp === "number" ? parsed.exp : null;
167
+ } catch {
168
+ return null;
169
+ }
170
+ }
171
+ function extractAuthToken(payload) {
172
+ if (typeof payload === "string" && payload.trim()) {
173
+ return payload.trim();
174
+ }
175
+ if (!payload || typeof payload !== "object") {
176
+ throw new Error("Unable to extract bearer token from TeamDynamix authentication response.");
177
+ }
178
+ const record = payload;
179
+ const candidateKeys = ["token", "Token", "accessToken", "AccessToken", "bearerToken", "TokenText", "value"];
180
+ for (const key of candidateKeys) {
181
+ const candidate = record[key];
182
+ if (typeof candidate === "string" && candidate.trim()) {
183
+ return candidate.trim();
184
+ }
185
+ }
186
+ throw new Error("Unable to extract bearer token from TeamDynamix authentication response.");
187
+ }
188
+
189
+ // src/tools/teamdynamix.domain-gateways.tools.ts
190
+ import { z as z4 } from "zod";
191
+
192
+ // src/schemas/teamdynamix/index.ts
193
+ import { z as z3 } from "zod";
194
+
195
+ // src/schemas/index.ts
196
+ import { z as z2 } from "zod";
197
+ var ResponseFormatSchema = z2.enum(["markdown", "json"]).default("markdown").describe("Output format for the response.");
198
+ var TextTransformModeSchema = z2.enum(["uppercase", "lowercase", "trim", "slug"]).describe("Transformation mode to apply to the input text.");
199
+
200
+ // src/schemas/teamdynamix/index.ts
201
+ var TeamDynamixAppIdSchema = z3.number().int().positive().describe("TeamDynamix application ID.");
202
+ var TeamDynamixGuidSchema = z3.string().uuid().describe("TeamDynamix GUID identifier.");
203
+ var TeamDynamixResponseFormatSchema = ResponseFormatSchema;
204
+ var TicketSearchSchema = z3.object({
205
+ Keywords: z3.string().optional().describe("Full-text search term."),
206
+ MaxResults: z3.number().int().min(1).max(1e3).optional().default(50).describe("Maximum results to return (1\u20131000)."),
207
+ StatusIDs: z3.array(z3.number().int()).optional().describe("Filter by ticket status IDs."),
208
+ TypeIDs: z3.array(z3.number().int()).optional().describe("Filter by ticket type IDs."),
209
+ PriorityIDs: z3.array(z3.number().int()).optional().describe("Filter by ticket priority IDs."),
210
+ UrgencyIDs: z3.array(z3.number().int()).optional().describe("Filter by ticket urgency IDs."),
211
+ ImpactIDs: z3.array(z3.number().int()).optional().describe("Filter by ticket impact IDs."),
212
+ AccountIDs: z3.array(z3.number().int()).optional().describe("Filter by account/department IDs."),
213
+ ResponsibleGroupIDs: z3.array(z3.number().int()).optional().describe("Filter by responsible group IDs."),
214
+ ResponsibleUids: z3.array(z3.string().uuid()).optional().describe("Filter by responsible user GUIDs."),
215
+ RequestorUids: z3.array(z3.string().uuid()).optional().describe("Filter by requestor user GUIDs."),
216
+ CreatedDateFrom: z3.string().optional().describe("ISO 8601 start date for creation date filter."),
217
+ CreatedDateTo: z3.string().optional().describe("ISO 8601 end date for creation date filter."),
218
+ ModifiedDateFrom: z3.string().optional().describe("ISO 8601 start date for last-modified filter."),
219
+ ModifiedDateTo: z3.string().optional().describe("ISO 8601 end date for last-modified filter."),
220
+ ClosedDateFrom: z3.string().optional().describe("ISO 8601 start date for closed date filter."),
221
+ ClosedDateTo: z3.string().optional().describe("ISO 8601 end date for closed date filter."),
222
+ SortBy: z3.string().optional().describe("Field name to sort results by."),
223
+ SortOrder: z3.enum(["A", "D"]).optional().describe("Sort order: A = ascending, D = descending.")
224
+ });
225
+ var TicketCreateSchema = z3.object({
226
+ TypeID: z3.number().int().positive().describe("Ticket type ID."),
227
+ Title: z3.string().min(1).describe("Ticket title/subject."),
228
+ AccountID: z3.number().int().positive().optional().describe("Account/department ID."),
229
+ StatusID: z3.number().int().nonnegative().optional().describe("Initial ticket status ID."),
230
+ PriorityID: z3.number().int().nonnegative().optional().describe("Ticket priority ID."),
231
+ UrgencyID: z3.number().int().nonnegative().optional().describe("Ticket urgency ID."),
232
+ ImpactID: z3.number().int().nonnegative().optional().describe("Ticket impact ID."),
233
+ SourceID: z3.number().int().nonnegative().optional().describe("Ticket source ID."),
234
+ Description: z3.string().optional().describe("Full description/body of the ticket (HTML supported)."),
235
+ RequestorUID: z3.string().uuid().optional().describe("GUID of the requestor."),
236
+ ResponsibleUID: z3.string().uuid().optional().describe("GUID of the responsible technician."),
237
+ ResponsibleGroupID: z3.number().int().positive().optional().describe("Responsible group ID."),
238
+ FormID: z3.number().int().positive().optional().describe("Ticket form ID."),
239
+ Attributes: z3.array(
240
+ z3.object({
241
+ ID: z3.number().int(),
242
+ Value: z3.string()
243
+ })
244
+ ).optional().describe("Custom attribute values.")
245
+ });
246
+ var TicketPatchSchema = z3.object({
247
+ TicketID: z3.number().int().positive().describe("Ticket ID to update."),
248
+ Attributes: z3.record(z3.string(), z3.string()).describe(
249
+ 'Fields to update as key/value pairs. Keys must be valid ticket field names (e.g. "StatusID", "Title", "ResponsibleUID").'
250
+ ),
251
+ NotifyRequestor: z3.boolean().optional().default(false).describe("Notify the requestor of the change."),
252
+ NotifyResponsible: z3.boolean().optional().default(false).describe("Notify the responsible technician of the change."),
253
+ Comments: z3.string().optional().describe("Comment to attach to this update."),
254
+ IsPrivate: z3.boolean().optional().default(false).describe("Whether the comment is private.")
255
+ });
256
+ var TicketCommentSchema = z3.object({
257
+ TicketID: z3.number().int().positive().describe("Ticket ID to comment on."),
258
+ Body: z3.string().min(1).describe("Comment body (HTML supported)."),
259
+ IsPrivate: z3.boolean().optional().default(false).describe("Whether this comment is private."),
260
+ NotifyRequestor: z3.boolean().optional().default(false).describe("Notify the requestor."),
261
+ NotifyResponsible: z3.boolean().optional().default(false).describe("Notify the responsible technician.")
262
+ });
263
+ var UserSearchSchema = z3.object({
264
+ SearchText: z3.string().optional().describe("Name, username, or email to search for."),
265
+ IsActive: z3.boolean().optional().describe("Filter by active (true) or inactive (false) users."),
266
+ IsEmployee: z3.boolean().optional().describe("Filter to employees only."),
267
+ AppID: z3.number().int().positive().optional().describe("Scope search to a specific application."),
268
+ MaxResults: z3.number().int().min(1).max(1e3).optional().default(25).describe("Maximum results (1\u20131000).")
269
+ });
270
+ var GroupSearchSchema = z3.object({
271
+ NameLike: z3.string().optional().describe("Partial group name to search."),
272
+ IsActive: z3.boolean().optional().describe("Filter by active (true) or inactive (false) groups."),
273
+ AppID: z3.number().int().positive().optional().describe("Scope search to a specific application.")
274
+ });
275
+ var KbArticleSearchSchema = z3.object({
276
+ SearchText: z3.string().optional().describe("Full-text search within articles."),
277
+ CategoryID: z3.number().int().positive().optional().describe("Filter by KB category ID."),
278
+ IsPublished: z3.boolean().optional().describe("Filter to published articles only."),
279
+ MaxResults: z3.number().int().min(1).max(500).optional().default(25).describe("Maximum results (1\u2013500).")
280
+ });
281
+ var AssetSearchSchema = z3.object({
282
+ SerialLike: z3.string().optional().describe("Partial serial number to match."),
283
+ TagLike: z3.string().optional().describe("Partial asset tag to match."),
284
+ SearchText: z3.string().optional().describe("General text search across asset fields."),
285
+ StatusIDs: z3.array(z3.number().int()).optional().describe("Filter by asset status IDs."),
286
+ OwnerUID: z3.string().uuid().optional().describe("Filter by asset owner GUID."),
287
+ UsingDepartmentID: z3.number().int().positive().optional().describe("Filter by using department ID."),
288
+ MaxResults: z3.number().int().min(1).max(1e3).optional().default(25).describe("Maximum results (1\u20131000).")
289
+ });
290
+ var ServiceSearchSchema = z3.object({
291
+ SearchText: z3.string().optional().describe("Full-text search across service fields."),
292
+ IsActive: z3.boolean().optional().describe("Filter to active services only."),
293
+ CategoryID: z3.number().int().positive().optional().describe("Filter by service category ID."),
294
+ MaxResults: z3.number().int().min(1).max(500).optional().default(25).describe("Maximum results (1\u2013500).")
295
+ });
296
+ var ProjectSearchSchema = z3.object({
297
+ NameLike: z3.string().optional().describe("Partial project name to search."),
298
+ TypeIDs: z3.array(z3.number().int()).optional().describe("Filter by project type IDs."),
299
+ IsActive: z3.boolean().optional().describe("Filter to active projects."),
300
+ ManagerUID: z3.string().uuid().optional().describe("Filter by project manager GUID."),
301
+ MaxResults: z3.number().int().min(1).max(500).optional().default(25).describe("Maximum results (1\u2013500).")
302
+ });
303
+ var TicketTaskCreateSchema = z3.object({
304
+ TicketID: z3.number().int().positive().describe("Parent ticket ID."),
305
+ Title: z3.string().min(1).describe("Task title."),
306
+ Description: z3.string().optional().describe("Task description."),
307
+ IsActive: z3.boolean().optional().default(true).describe("Whether the task is active."),
308
+ AssignedUID: z3.string().uuid().optional().describe("GUID of the assigned user."),
309
+ AssignedGroupID: z3.number().int().positive().optional().describe("Assigned group ID."),
310
+ EstimatedMinutes: z3.number().int().nonnegative().optional().describe("Estimated minutes to complete."),
311
+ StartDate: z3.string().optional().describe("Task start date (ISO 8601)."),
312
+ EndDate: z3.string().optional().describe("Task due date (ISO 8601).")
313
+ });
314
+ var KbArticleCreateSchema = z3.object({
315
+ Subject: z3.string().min(1).describe("Article title/subject."),
316
+ Body: z3.string().min(1).describe("Article body content (HTML supported)."),
317
+ Summary: z3.string().optional().describe("Short article summary."),
318
+ CategoryID: z3.number().int().positive().optional().describe("KB category ID."),
319
+ IsPublished: z3.boolean().optional().default(false).describe("Whether to publish the article immediately."),
320
+ Tags: z3.string().optional().describe("Comma-separated tags for the article.")
321
+ });
322
+ var KbArticleUpdateSchema = KbArticleCreateSchema.partial().extend({
323
+ ArticleID: z3.number().int().positive().describe("KB article ID to update.")
324
+ });
325
+ var CiSearchSchema = z3.object({
326
+ SearchText: z3.string().optional().describe("Full-text search across CI fields."),
327
+ TypeIDs: z3.array(z3.number().int()).optional().describe("Filter by CI type IDs."),
328
+ OwnerUID: z3.string().uuid().optional().describe("Filter by owner GUID."),
329
+ MaxResults: z3.number().int().min(1).max(1e3).optional().default(25).describe("Maximum results (1\u20131000).")
330
+ });
331
+ var CustomAttributeComponentIdSchema = z3.number().int().positive().describe(
332
+ "TeamDynamix component ID for the attribute context. Common values: 9 = Ticket, 27 = Asset, 63 = KB Article, 31 = Person."
333
+ );
334
+ var ProjectIssueCreateSchema = z3.object({
335
+ Title: z3.string().min(1).describe("Issue title."),
336
+ Description: z3.string().optional().describe("Issue description."),
337
+ AssignedUID: z3.string().uuid().optional().describe("GUID of the assigned user."),
338
+ StatusID: z3.number().int().nonnegative().optional().describe("Issue status ID."),
339
+ PriorityID: z3.number().int().nonnegative().optional().describe("Issue priority ID."),
340
+ DueDate: z3.string().optional().describe("Issue due date (ISO 8601).")
341
+ });
342
+ var ProjectRiskCreateSchema = z3.object({
343
+ Title: z3.string().min(1).describe("Risk title."),
344
+ Description: z3.string().optional().describe("Risk description."),
345
+ AssignedUID: z3.string().uuid().optional().describe("GUID of the risk owner."),
346
+ StatusID: z3.number().int().nonnegative().optional().describe("Risk status ID."),
347
+ Probability: z3.number().int().min(1).max(100).optional().describe("Probability percentage (1\u2013100)."),
348
+ Impact: z3.number().int().min(1).max(5).optional().describe("Impact level (1 = low, 5 = critical)."),
349
+ DueDate: z3.string().optional().describe("Risk resolution due date (ISO 8601).")
350
+ });
351
+ var TimeEntryQuerySchema = z3.object({
352
+ StartDate: z3.string().describe("Start date for time entries query (ISO 8601 date, e.g. 2026-01-01)."),
353
+ EndDate: z3.string().describe("End date for time entries query (ISO 8601 date, e.g. 2026-01-31).")
354
+ });
355
+ var TeamDynamixEntitySchema = z3.record(z3.string(), z3.unknown()).describe("Base TeamDynamix entity with flexible additional fields");
356
+ var TeamDynamixNamedEntitySchema = z3.object({
357
+ ID: z3.number().int().positive("ID must be a positive integer"),
358
+ Name: z3.string().optional()
359
+ }).catchall(z3.unknown()).describe("TeamDynamix entity with ID and optional Name");
360
+ var TeamDynamixApplicationSchema = TeamDynamixNamedEntitySchema.extend({
361
+ Name: z3.string().optional()
362
+ }).describe("TeamDynamix Application entity");
363
+ var TeamDynamixTicketSchema = TeamDynamixNamedEntitySchema.extend({
364
+ Title: z3.string().optional(),
365
+ Description: z3.string().optional(),
366
+ StatusID: z3.number().int().optional(),
367
+ TypeID: z3.number().int().optional()
368
+ }).describe("TeamDynamix Ticket entity");
369
+ var TeamDynamixKbArticleSchema = TeamDynamixNamedEntitySchema.extend({
370
+ Subject: z3.string().optional(),
371
+ Body: z3.string().optional(),
372
+ IsPublished: z3.boolean().optional()
373
+ }).describe("TeamDynamix Knowledge Base Article entity");
374
+ var TeamDynamixAssetSchema = TeamDynamixNamedEntitySchema.extend({
375
+ Tag: z3.string().optional(),
376
+ StatusID: z3.number().int().optional()
377
+ }).describe("TeamDynamix Asset entity");
378
+ var TeamDynamixProjectSchema = TeamDynamixNamedEntitySchema.extend({
379
+ Status: z3.string().optional()
380
+ }).describe("TeamDynamix Project entity");
381
+ var TeamDynamixGroupSchema = TeamDynamixNamedEntitySchema.extend({
382
+ Description: z3.string().optional()
383
+ }).describe("TeamDynamix Group entity");
384
+ var TeamDynamixUserSchema = z3.object({
385
+ UID: z3.string({ error: "User UID is required" }),
386
+ FullName: z3.string().optional(),
387
+ Email: z3.string().email().optional(),
388
+ IsActive: z3.boolean().optional()
389
+ }).catchall(z3.unknown()).describe("TeamDynamix User entity");
390
+ var TeamDynamixListResponseSchema = z3.array(z3.record(z3.string(), z3.unknown())).describe("Array of TeamDynamix entities");
391
+ var TeamDynamixSingleResponseSchema = z3.record(z3.string(), z3.unknown()).describe("Single TeamDynamix entity response");
392
+
393
+ // src/services/teamdynamix/client.service.ts
394
+ function assertWriteToolsEnabled(config) {
395
+ if (!config.enableWriteTools) {
396
+ throw new Error(
397
+ "Write tools are disabled. Set TEAMDYNAMIX_ENABLE_WRITE_TOOLS=true in your environment to enable ticket creation, updates, comments, and other write operations."
398
+ );
399
+ }
400
+ }
401
+ function sleep(ms) {
402
+ return new Promise((resolve) => {
403
+ setTimeout(resolve, ms);
404
+ });
405
+ }
406
+ function normalizePath(path) {
407
+ return path.startsWith("/") ? path : `/${path}`;
408
+ }
409
+ var TeamDynamixClient = class {
410
+ constructor(config) {
411
+ this.config = config;
412
+ }
413
+ cachedToken = null;
414
+ async getCurrentUser() {
415
+ return await this.requestJson("/api/auth/getuser");
416
+ }
417
+ async listApplications() {
418
+ return await this.requestJson("/api/applications");
419
+ }
420
+ // -------------------------------------------------------------------------
421
+ // Discovery / Enumeration domains
422
+ // -------------------------------------------------------------------------
423
+ async listAccounts(appId) {
424
+ const path = appId ? `/api/${appId}/accounts` : "/api/accounts";
425
+ return await this.requestJson(path);
426
+ }
427
+ async getAccount(accountId) {
428
+ return await this.requestJson(`/api/accounts/${accountId}`);
429
+ }
430
+ async listLocations() {
431
+ return await this.requestJson("/api/locations");
432
+ }
433
+ async listFunctionalRoles() {
434
+ return await this.requestJson("/api/functionalroles");
435
+ }
436
+ async listCustomAttributes(componentId, appId, associatedTypeId) {
437
+ const qs = new URLSearchParams({ componentId: String(componentId) });
438
+ if (appId !== void 0) qs.set("appId", String(appId));
439
+ if (associatedTypeId !== void 0) qs.set("associatedTypeId", String(associatedTypeId));
440
+ return await this.requestJson(`/api/attributes/custom?${qs}`);
441
+ }
442
+ async listTicketStatuses(appId) {
443
+ return await this.requestJson(`/api/${appId}/tickets/statuses`);
444
+ }
445
+ async listTicketTypes(appId) {
446
+ return await this.requestJson(`/api/${appId}/tickets/types`);
447
+ }
448
+ async listTicketPriorities(appId) {
449
+ return await this.requestJson(`/api/${appId}/tickets/priorities`);
450
+ }
451
+ async listTicketUrgencies(appId) {
452
+ return await this.requestJson(`/api/${appId}/tickets/urgencies`);
453
+ }
454
+ async listTicketImpacts(appId) {
455
+ return await this.requestJson(`/api/${appId}/tickets/impacts`);
456
+ }
457
+ async listTicketSources(appId) {
458
+ return await this.requestJson(`/api/${appId}/tickets/sources`);
459
+ }
460
+ async getTicket(appId, ticketId) {
461
+ return await this.requestJson(`/api/${appId}/tickets/${ticketId}`);
462
+ }
463
+ async searchTickets(appId, body) {
464
+ return await this.requestJson(`/api/${appId}/tickets/search`, {
465
+ method: "POST",
466
+ body: JSON.stringify(body)
467
+ });
468
+ }
469
+ async createTicket(appId, body, notifyRequestor = false, notifyResponsible = false) {
470
+ return await this.requestJson(
471
+ `/api/${appId}/tickets?NotifyRequestor=${notifyRequestor}&NotifyResponsible=${notifyResponsible}`,
472
+ {
473
+ method: "POST",
474
+ body: JSON.stringify(body)
475
+ }
476
+ );
477
+ }
478
+ async updateTicket(appId, ticketId, body, notifyRequestor = false, notifyResponsible = false, comments = "", isPrivate = false) {
479
+ const qs = new URLSearchParams({
480
+ NotifyRequestor: String(notifyRequestor),
481
+ NotifyResponsible: String(notifyResponsible),
482
+ ...comments ? { Comments: comments } : {},
483
+ IsPrivate: String(isPrivate)
484
+ });
485
+ return await this.requestJson(`/api/${appId}/tickets/${ticketId}?${qs}`, {
486
+ method: "PATCH",
487
+ body: JSON.stringify(body)
488
+ });
489
+ }
490
+ async addTicketComment(appId, ticketId, body, isPrivate = false, notifyRequestor = false, notifyResponsible = false) {
491
+ return await this.requestJson(`/api/${appId}/tickets/${ticketId}/feed`, {
492
+ method: "POST",
493
+ body: JSON.stringify({
494
+ Body: body,
495
+ IsPrivate: isPrivate,
496
+ NotifyRequestor: notifyRequestor,
497
+ NotifyResponsible: notifyResponsible
498
+ })
499
+ });
500
+ }
501
+ async getTicketFeed(appId, ticketId) {
502
+ return await this.requestJson(`/api/${appId}/tickets/${ticketId}/feed`);
503
+ }
504
+ async getTicketTasks(appId, ticketId) {
505
+ return await this.requestJson(`/api/${appId}/tickets/${ticketId}/tasks`);
506
+ }
507
+ async createTicketTask(appId, ticketId, body) {
508
+ return await this.requestJson(`/api/${appId}/tickets/${ticketId}/tasks`, {
509
+ method: "POST",
510
+ body: JSON.stringify(body)
511
+ });
512
+ }
513
+ async listTicketAssets(appId, ticketId) {
514
+ return await this.requestJson(`/api/${appId}/tickets/${ticketId}/assets`);
515
+ }
516
+ async addTicketAsset(appId, ticketId, assetId) {
517
+ return await this.requestJson(`/api/${appId}/tickets/${ticketId}/assets/${assetId}`, {
518
+ method: "POST",
519
+ body: JSON.stringify({})
520
+ });
521
+ }
522
+ async removeTicketAsset(appId, ticketId, assetId) {
523
+ await this.requestJson(`/api/${appId}/tickets/${ticketId}/assets/${assetId}`, { method: "DELETE" });
524
+ }
525
+ // -------------------------------------------------------------------------
526
+ // People & Groups
527
+ // -------------------------------------------------------------------------
528
+ async getUser(uid) {
529
+ return await this.requestJson(`/api/people/${encodeURIComponent(uid)}`);
530
+ }
531
+ async searchUsers(body) {
532
+ return await this.requestJson("/api/people/search", {
533
+ method: "POST",
534
+ body: JSON.stringify(body)
535
+ });
536
+ }
537
+ async getGroup(groupId) {
538
+ return await this.requestJson(`/api/groups/${groupId}`);
539
+ }
540
+ async searchGroups(body) {
541
+ return await this.requestJson("/api/groups/search", {
542
+ method: "POST",
543
+ body: JSON.stringify(body)
544
+ });
545
+ }
546
+ async getGroupMembers(groupId) {
547
+ return await this.requestJson(`/api/groups/${groupId}/members`);
548
+ }
549
+ // -------------------------------------------------------------------------
550
+ // Knowledge Base
551
+ // -------------------------------------------------------------------------
552
+ async getKbArticle(appId, articleId) {
553
+ return await this.requestJson(`/api/${appId}/knowledgebase/${articleId}`);
554
+ }
555
+ async searchKbArticles(appId, body) {
556
+ return await this.requestJson(`/api/${appId}/knowledgebase/search`, {
557
+ method: "POST",
558
+ body: JSON.stringify(body)
559
+ });
560
+ }
561
+ async listKbCategories(appId) {
562
+ return await this.requestJson(`/api/${appId}/knowledgebase/categories`);
563
+ }
564
+ async createKbArticle(appId, body) {
565
+ return await this.requestJson(`/api/${appId}/knowledgebase`, {
566
+ method: "POST",
567
+ body: JSON.stringify(body)
568
+ });
569
+ }
570
+ async updateKbArticle(appId, articleId, body) {
571
+ return await this.requestJson(`/api/${appId}/knowledgebase/${articleId}`, {
572
+ method: "PUT",
573
+ body: JSON.stringify(body)
574
+ });
575
+ }
576
+ // -------------------------------------------------------------------------
577
+ // Assets / CMDB
578
+ // -------------------------------------------------------------------------
579
+ async getAsset(appId, assetId) {
580
+ return await this.requestJson(`/api/${appId}/assets/${assetId}`);
581
+ }
582
+ async searchAssets(appId, body) {
583
+ return await this.requestJson(`/api/${appId}/assets/search`, {
584
+ method: "POST",
585
+ body: JSON.stringify(body)
586
+ });
587
+ }
588
+ async listAssetStatuses(appId) {
589
+ return await this.requestJson(`/api/${appId}/assets/statuses`);
590
+ }
591
+ async listProductModels(appId) {
592
+ return await this.requestJson(`/api/${appId}/assets/models`);
593
+ }
594
+ async listVendors(appId) {
595
+ return await this.requestJson(`/api/${appId}/assets/vendors`);
596
+ }
597
+ async getConfigurationItem(appId, ciId) {
598
+ return await this.requestJson(`/api/${appId}/cmdb/${ciId}`);
599
+ }
600
+ async searchConfigurationItems(appId, body) {
601
+ return await this.requestJson(`/api/${appId}/cmdb/search`, {
602
+ method: "POST",
603
+ body: JSON.stringify(body)
604
+ });
605
+ }
606
+ async listCiTypes(appId) {
607
+ return await this.requestJson(`/api/${appId}/cmdb/types`);
608
+ }
609
+ async listCiRelationshipTypes() {
610
+ return await this.requestJson("/api/cmdb/relationshiptypes");
611
+ }
612
+ // -------------------------------------------------------------------------
613
+ // Service Catalog
614
+ // -------------------------------------------------------------------------
615
+ async listServiceCatalog(appId) {
616
+ return await this.requestJson(`/api/${appId}/services`);
617
+ }
618
+ async getService(appId, serviceId) {
619
+ return await this.requestJson(`/api/${appId}/services/${serviceId}`);
620
+ }
621
+ async searchServices(appId, body) {
622
+ return await this.requestJson(`/api/${appId}/services/search`, {
623
+ method: "POST",
624
+ body: JSON.stringify(body)
625
+ });
626
+ }
627
+ // -------------------------------------------------------------------------
628
+ // Projects
629
+ // -------------------------------------------------------------------------
630
+ async getProject(projectId) {
631
+ return await this.requestJson(`/api/projects/${projectId}`);
632
+ }
633
+ async searchProjects(body) {
634
+ return await this.requestJson("/api/projects/search", {
635
+ method: "POST",
636
+ body: JSON.stringify(body)
637
+ });
638
+ }
639
+ async listProjectTypes() {
640
+ return await this.requestJson("/api/projects/types");
641
+ }
642
+ async getProjectPlans(projectId) {
643
+ return await this.requestJson(`/api/projects/${projectId}/plans`);
644
+ }
645
+ async getProjectIssues(projectId) {
646
+ return await this.requestJson(`/api/projects/${projectId}/issues`);
647
+ }
648
+ async getProjectRisks(projectId) {
649
+ return await this.requestJson(`/api/projects/${projectId}/risks`);
650
+ }
651
+ async createProjectIssue(projectId, body) {
652
+ return await this.requestJson(`/api/projects/${projectId}/issues`, {
653
+ method: "POST",
654
+ body: JSON.stringify(body)
655
+ });
656
+ }
657
+ async createProjectRisk(projectId, body) {
658
+ return await this.requestJson(`/api/projects/${projectId}/risks`, {
659
+ method: "POST",
660
+ body: JSON.stringify(body)
661
+ });
662
+ }
663
+ async listTimeTypes() {
664
+ return await this.requestJson("/api/time/types");
665
+ }
666
+ async getMyTimeEntries(startDate, endDate) {
667
+ const qs = new URLSearchParams({ startDate, endDate });
668
+ return await this.requestJson(`/api/time/entries?${qs}`);
669
+ }
670
+ async getTicketContacts(appId, ticketId) {
671
+ return await this.requestJson(`/api/${appId}/tickets/${ticketId}/contacts`);
672
+ }
673
+ async addTicketContact(appId, ticketId, contactUid) {
674
+ return await this.requestJson(`/api/${appId}/tickets/${ticketId}/contacts/${contactUid}`, {
675
+ method: "PUT"
676
+ });
677
+ }
678
+ async removeTicketContact(appId, ticketId, contactUid) {
679
+ await this.requestJson(`/api/${appId}/tickets/${ticketId}/contacts/${contactUid}`, {
680
+ method: "DELETE"
681
+ });
682
+ }
683
+ async listServiceCategories(appId) {
684
+ return await this.requestJson(`/api/${appId}/services/categories`);
685
+ }
686
+ async requestJson(path, options = {}) {
687
+ const headers = new Headers(options.headers);
688
+ headers.set("Accept", "application/json");
689
+ if (options.body && !headers.has("Content-Type") && !(options.body instanceof FormData)) {
690
+ headers.set("Content-Type", "application/json");
691
+ }
692
+ const token = await this.getBearerToken(options.requireAdmin ?? false);
693
+ headers.set("Authorization", `Bearer ${token}`);
694
+ const requestInit = {
695
+ method: options.method ?? "GET",
696
+ headers,
697
+ body: options.body
698
+ };
699
+ const endpoint = `${this.config.baseUrl}${normalizePath(path)}`;
700
+ const cappedRetries = Math.min(this.config.maxRetries, TEAMDYNAMIX_MAX_RETRY_ATTEMPTS);
701
+ for (let attempt = 0; attempt <= cappedRetries; attempt += 1) {
702
+ const response = await fetch(endpoint, requestInit);
703
+ if (response.status === 429 && attempt < cappedRetries) {
704
+ const rateLimit = parseRateLimit(response.headers);
705
+ if (LOG_LEVEL === "debug") {
706
+ console.error(
707
+ `[teamdynamix-mcp] rate limited on ${normalizePath(path)}; retry ${attempt + 1}/${cappedRetries} in ${rateLimit.waitMs}ms`
708
+ );
709
+ }
710
+ await sleep(rateLimit.waitMs);
711
+ continue;
712
+ }
713
+ if (!response.ok) {
714
+ await response.text();
715
+ throw new Error(`TeamDynamix request failed (${response.status}) for ${normalizePath(path)}.`);
716
+ }
717
+ const contentType = response.headers.get("content-type") ?? "";
718
+ if (contentType.includes("application/json")) {
719
+ const data = await response.json();
720
+ if (Array.isArray(data)) {
721
+ TeamDynamixListResponseSchema.parse(data);
722
+ } else if (typeof data === "object" && data !== null) {
723
+ TeamDynamixSingleResponseSchema.parse(data);
724
+ }
725
+ return data;
726
+ }
727
+ const text = await response.text();
728
+ return text;
729
+ }
730
+ throw new Error(`TeamDynamix request exceeded retry budget for ${normalizePath(path)}.`);
731
+ }
732
+ async getBearerToken(requireAdmin) {
733
+ const configStatus = getTeamDynamixConfigStatus(this.config);
734
+ if (!configStatus.configured) {
735
+ throw new Error(
736
+ `TeamDynamix is not configured. Missing: ${configStatus.missing.join(", ")}. Populate the required environment variables in .env before using TeamDynamix tools.`
737
+ );
738
+ }
739
+ if (requireAdmin && this.config.authMode !== "admin") {
740
+ throw new Error(
741
+ "This TeamDynamix action requires admin authentication, but TEAMDYNAMIX_AUTH_MODE is not set to admin."
742
+ );
743
+ }
744
+ const expiresAtMs = this.cachedToken?.expiresAtMs;
745
+ if (this.cachedToken && (!expiresAtMs || expiresAtMs > Date.now() + 6e4)) {
746
+ return this.cachedToken.token;
747
+ }
748
+ const token = await this.login();
749
+ return token;
750
+ }
751
+ async login() {
752
+ if (!this.config.baseUrl) {
753
+ throw new Error("TeamDynamix base URL is not configured. Set TEAMDYNAMIX_BASE_URL.");
754
+ }
755
+ const isAdmin = this.config.authMode === "admin";
756
+ const loginPath = isAdmin ? "/api/auth/loginadmin" : "/api/auth/login";
757
+ const body = isAdmin ? JSON.stringify({ BEID: this.config.beid, WebServicesKey: this.config.webServicesKey }) : JSON.stringify({ username: this.config.username, password: this.config.password });
758
+ const response = await fetch(`${this.config.baseUrl}${loginPath}`, {
759
+ method: "POST",
760
+ headers: {
761
+ Accept: "application/json, text/plain;q=0.9",
762
+ "Content-Type": "application/json"
763
+ },
764
+ body
765
+ });
766
+ if (!response.ok) {
767
+ await response.text();
768
+ throw new Error(`TeamDynamix authentication failed (${response.status}).`);
769
+ }
770
+ const contentType = response.headers.get("content-type") ?? "";
771
+ const payload = contentType.includes("application/json") ? await response.json() : await response.text();
772
+ const token = extractAuthToken(payload);
773
+ const expiryEpochSeconds = decodeJwtExpiryEpochSeconds(token);
774
+ this.cachedToken = {
775
+ token,
776
+ expiresAtMs: expiryEpochSeconds ? expiryEpochSeconds * 1e3 : null
777
+ };
778
+ return token;
779
+ }
780
+ };
781
+ function createConfiguredTeamDynamixClient() {
782
+ return new TeamDynamixClient(getTeamDynamixConfig());
783
+ }
784
+
785
+ // src/tools/teamdynamix.domain-gateways.tools.ts
786
+ var teamdynamixDiscoveryActionSchema = z4.enum([
787
+ "server_status",
788
+ "get_current_user",
789
+ "list_applications",
790
+ "list_ticket_statuses"
791
+ ]);
792
+ var teamdynamixTicketActionSchema = z4.enum([
793
+ "list_ticket_types",
794
+ "list_ticket_priorities",
795
+ "list_ticket_urgencies",
796
+ "list_ticket_impacts",
797
+ "list_ticket_sources",
798
+ "get_ticket",
799
+ "search_tickets",
800
+ "create_ticket",
801
+ "update_ticket",
802
+ "add_ticket_comment",
803
+ "get_ticket_feed"
804
+ ]);
805
+ var teamdynamixTicketRelationshipActionSchema = z4.enum([
806
+ "get_ticket_tasks",
807
+ "create_ticket_task",
808
+ "list_ticket_assets",
809
+ "add_ticket_asset",
810
+ "remove_ticket_asset",
811
+ "get_ticket_contacts",
812
+ "add_ticket_contact",
813
+ "remove_ticket_contact"
814
+ ]);
815
+ var teamdynamixPeopleActionSchema = z4.enum([
816
+ "get_user",
817
+ "search_users",
818
+ "get_group",
819
+ "search_groups",
820
+ "get_group_members"
821
+ ]);
822
+ var teamdynamixKnowledgeBaseActionSchema = z4.enum([
823
+ "get_kb_article",
824
+ "search_kb_articles",
825
+ "list_kb_categories",
826
+ "create_kb_article",
827
+ "update_kb_article"
828
+ ]);
829
+ var teamdynamixAssetsActionSchema = z4.enum([
830
+ "get_asset",
831
+ "search_assets",
832
+ "list_asset_statuses",
833
+ "list_product_models"
834
+ ]);
835
+ var teamdynamixCmdbActionSchema = z4.enum([
836
+ "get_ci",
837
+ "search_cis",
838
+ "list_ci_types",
839
+ "list_ci_relationship_types",
840
+ "list_vendors"
841
+ ]);
842
+ var teamdynamixServicesActionSchema = z4.enum([
843
+ "list_services",
844
+ "get_service",
845
+ "search_services",
846
+ "list_service_categories"
847
+ ]);
848
+ var teamdynamixProjectsActionSchema = z4.enum([
849
+ "get_project",
850
+ "search_projects",
851
+ "list_project_types",
852
+ "get_project_plans",
853
+ "get_project_issues",
854
+ "get_project_risks",
855
+ "create_project_issue",
856
+ "create_project_risk"
857
+ ]);
858
+ var teamdynamixTimeActionSchema = z4.enum(["list_time_types", "get_my_time_entries"]);
859
+ var teamdynamixReferenceDataActionSchema = z4.enum([
860
+ "list_accounts",
861
+ "get_account",
862
+ "list_locations",
863
+ "list_functional_roles",
864
+ "list_custom_attributes"
865
+ ]);
866
+ var gatewayPayloadSchema = z4.record(z4.string(), z4.unknown()).default({});
867
+ function render(data, responseFormat) {
868
+ if (responseFormat === "json") return JSON.stringify(data, null, 2);
869
+ if (typeof data === "string") return data;
870
+ return JSON.stringify(data, null, 2);
871
+ }
872
+ function messageFromError(error) {
873
+ return error instanceof Error ? error.message : String(error);
874
+ }
875
+ function toStructuredContent(payload) {
876
+ if (typeof payload === "object" && payload !== null && !Array.isArray(payload)) {
877
+ return payload;
878
+ }
879
+ return {
880
+ data: payload
881
+ };
882
+ }
883
+ function parsePayload(schema, payload, action) {
884
+ const parsed = schema.safeParse(payload);
885
+ if (!parsed.success) {
886
+ const details = parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
887
+ throw new Error(`Invalid payload for action "${action}": ${details}`);
888
+ }
889
+ return parsed.data;
890
+ }
891
+ function toSuccessResponse(payload, responseFormat) {
892
+ return {
893
+ content: [{ type: "text", text: render(payload, responseFormat) }],
894
+ structuredContent: toStructuredContent(payload)
895
+ };
896
+ }
897
+ function toErrorResponse(error) {
898
+ const message = messageFromError(error);
899
+ return {
900
+ content: [{ type: "text", text: `Error: ${message}` }],
901
+ structuredContent: {
902
+ ok: false,
903
+ kind: "unknown",
904
+ message
905
+ },
906
+ isError: true
907
+ };
908
+ }
909
+ var teamdynamixDiscoveryTool = `${TEAMDYNAMIX_TOOL_PREFIX}_discovery`;
910
+ var teamdynamixTicketsTool = `${TEAMDYNAMIX_TOOL_PREFIX}_tickets`;
911
+ var teamdynamixTicketRelationshipsTool = `${TEAMDYNAMIX_TOOL_PREFIX}_ticket_relationships`;
912
+ var teamdynamixPeopleTool = `${TEAMDYNAMIX_TOOL_PREFIX}_people`;
913
+ var teamdynamixKnowledgeBaseTool = `${TEAMDYNAMIX_TOOL_PREFIX}_knowledge_base`;
914
+ var teamdynamixAssetsTool = `${TEAMDYNAMIX_TOOL_PREFIX}_assets`;
915
+ var teamdynamixCmdbTool = `${TEAMDYNAMIX_TOOL_PREFIX}_cmdb`;
916
+ var teamdynamixServicesTool = `${TEAMDYNAMIX_TOOL_PREFIX}_services`;
917
+ var teamdynamixProjectsTool = `${TEAMDYNAMIX_TOOL_PREFIX}_projects`;
918
+ var teamdynamixTimeTool = `${TEAMDYNAMIX_TOOL_PREFIX}_time`;
919
+ var teamdynamixReferenceDataTool = `${TEAMDYNAMIX_TOOL_PREFIX}_reference_data`;
920
+ var TEAMDYNAMIX_GATEWAY_SURFACE = {
921
+ discovery: {
922
+ tool: teamdynamixDiscoveryTool,
923
+ actions: teamdynamixDiscoveryActionSchema.options
924
+ },
925
+ tickets: {
926
+ tool: teamdynamixTicketsTool,
927
+ actions: teamdynamixTicketActionSchema.options
928
+ },
929
+ ticketRelationships: {
930
+ tool: teamdynamixTicketRelationshipsTool,
931
+ actions: teamdynamixTicketRelationshipActionSchema.options
932
+ },
933
+ people: {
934
+ tool: teamdynamixPeopleTool,
935
+ actions: teamdynamixPeopleActionSchema.options
936
+ },
937
+ knowledgeBase: {
938
+ tool: teamdynamixKnowledgeBaseTool,
939
+ actions: teamdynamixKnowledgeBaseActionSchema.options
940
+ },
941
+ assets: {
942
+ tool: teamdynamixAssetsTool,
943
+ actions: teamdynamixAssetsActionSchema.options
944
+ },
945
+ cmdb: {
946
+ tool: teamdynamixCmdbTool,
947
+ actions: teamdynamixCmdbActionSchema.options
948
+ },
949
+ services: {
950
+ tool: teamdynamixServicesTool,
951
+ actions: teamdynamixServicesActionSchema.options
952
+ },
953
+ projects: {
954
+ tool: teamdynamixProjectsTool,
955
+ actions: teamdynamixProjectsActionSchema.options
956
+ },
957
+ time: {
958
+ tool: teamdynamixTimeTool,
959
+ actions: teamdynamixTimeActionSchema.options
960
+ },
961
+ referenceData: {
962
+ tool: teamdynamixReferenceDataTool,
963
+ actions: teamdynamixReferenceDataActionSchema.options
964
+ }
965
+ };
966
+ function registerTeamDynamixDomainGatewayTools(server2) {
967
+ registerTeamDynamixDiscoveryGateway(server2);
968
+ registerTeamDynamixTicketsGateway(server2);
969
+ registerTeamDynamixTicketRelationshipsGateway(server2);
970
+ registerTeamDynamixPeopleGateway(server2);
971
+ registerTeamDynamixKnowledgeBaseGateway(server2);
972
+ registerTeamDynamixAssetsGateway(server2);
973
+ registerTeamDynamixCmdbGateway(server2);
974
+ registerTeamDynamixServicesGateway(server2);
975
+ registerTeamDynamixProjectsGateway(server2);
976
+ registerTeamDynamixTimeGateway(server2);
977
+ registerTeamDynamixReferenceDataGateway(server2);
978
+ }
979
+ function registerTeamDynamixDiscoveryGateway(server2) {
980
+ server2.registerTool(
981
+ teamdynamixDiscoveryTool,
982
+ {
983
+ title: "TeamDynamix Discovery Gateway",
984
+ description: "Gateway for discovery operations: server status, current user, applications, and ticket status lookup.",
985
+ inputSchema: {
986
+ action: teamdynamixDiscoveryActionSchema,
987
+ payload: gatewayPayloadSchema,
988
+ response_format: TeamDynamixResponseFormatSchema
989
+ },
990
+ annotations: { readOnlyHint: true, idempotentHint: false, destructiveHint: false, openWorldHint: true }
991
+ },
992
+ async ({ action, payload, response_format }) => {
993
+ try {
994
+ const client = createConfiguredTeamDynamixClient();
995
+ switch (action) {
996
+ case "server_status": {
997
+ const config = getTeamDynamixConfig();
998
+ const status = getTeamDynamixConfigStatus(config);
999
+ const result = {
1000
+ status,
1001
+ config: redactTeamDynamixConfig(config),
1002
+ gateway: "discovery",
1003
+ actions: TEAMDYNAMIX_GATEWAY_SURFACE.discovery.actions
1004
+ };
1005
+ return toSuccessResponse(result, response_format);
1006
+ }
1007
+ case "get_current_user": {
1008
+ const user = await client.getCurrentUser();
1009
+ return toSuccessResponse(user, response_format);
1010
+ }
1011
+ case "list_applications": {
1012
+ const applications = await client.listApplications();
1013
+ return toSuccessResponse({ count: applications.length, applications }, response_format);
1014
+ }
1015
+ case "list_ticket_statuses": {
1016
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema.optional() }), payload, action);
1017
+ const config = getTeamDynamixConfig();
1018
+ const effectiveAppId = parsed.app_id ?? config.defaultTicketAppId;
1019
+ if (!effectiveAppId) {
1020
+ throw new Error(
1021
+ "No TeamDynamix ticket application ID was provided. Supply payload.app_id or configure TEAMDYNAMIX_DEFAULT_TICKET_APP_ID."
1022
+ );
1023
+ }
1024
+ const statuses = await client.listTicketStatuses(effectiveAppId);
1025
+ return toSuccessResponse({ appId: effectiveAppId, count: statuses.length, statuses }, response_format);
1026
+ }
1027
+ }
1028
+ } catch (error) {
1029
+ return toErrorResponse(error);
1030
+ }
1031
+ }
1032
+ );
1033
+ }
1034
+ function registerTeamDynamixTicketsGateway(server2) {
1035
+ server2.registerTool(
1036
+ teamdynamixTicketsTool,
1037
+ {
1038
+ title: "TeamDynamix Tickets Gateway",
1039
+ description: "Gateway for ticket metadata, read/search, create/update, comments, and feed operations.",
1040
+ inputSchema: {
1041
+ action: teamdynamixTicketActionSchema,
1042
+ payload: gatewayPayloadSchema,
1043
+ response_format: TeamDynamixResponseFormatSchema
1044
+ },
1045
+ annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: true, openWorldHint: true }
1046
+ },
1047
+ async ({ action, payload, response_format }) => {
1048
+ try {
1049
+ const client = createConfiguredTeamDynamixClient();
1050
+ switch (action) {
1051
+ case "list_ticket_types": {
1052
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1053
+ const types = await client.listTicketTypes(parsed.app_id);
1054
+ return toSuccessResponse({ appId: parsed.app_id, types }, response_format);
1055
+ }
1056
+ case "list_ticket_priorities": {
1057
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1058
+ const priorities = await client.listTicketPriorities(parsed.app_id);
1059
+ return toSuccessResponse({ appId: parsed.app_id, priorities }, response_format);
1060
+ }
1061
+ case "list_ticket_urgencies": {
1062
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1063
+ const urgencies = await client.listTicketUrgencies(parsed.app_id);
1064
+ return toSuccessResponse({ appId: parsed.app_id, urgencies }, response_format);
1065
+ }
1066
+ case "list_ticket_impacts": {
1067
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1068
+ const impacts = await client.listTicketImpacts(parsed.app_id);
1069
+ return toSuccessResponse({ appId: parsed.app_id, impacts }, response_format);
1070
+ }
1071
+ case "list_ticket_sources": {
1072
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1073
+ const sources = await client.listTicketSources(parsed.app_id);
1074
+ return toSuccessResponse({ appId: parsed.app_id, sources }, response_format);
1075
+ }
1076
+ case "get_ticket": {
1077
+ const parsed = parsePayload(
1078
+ z4.object({ app_id: TeamDynamixAppIdSchema, ticket_id: z4.number().int().positive() }),
1079
+ payload,
1080
+ action
1081
+ );
1082
+ const ticket = await client.getTicket(parsed.app_id, parsed.ticket_id);
1083
+ return toSuccessResponse(ticket, response_format);
1084
+ }
1085
+ case "search_tickets": {
1086
+ const parsed = parsePayload(
1087
+ z4.object({ app_id: TeamDynamixAppIdSchema, search: TicketSearchSchema }),
1088
+ payload,
1089
+ action
1090
+ );
1091
+ const tickets = await client.searchTickets(parsed.app_id, parsed.search);
1092
+ return toSuccessResponse({ appId: parsed.app_id, count: tickets.length, tickets }, response_format);
1093
+ }
1094
+ case "create_ticket": {
1095
+ const parsed = parsePayload(
1096
+ z4.object({
1097
+ app_id: TeamDynamixAppIdSchema,
1098
+ ticket: TicketCreateSchema,
1099
+ notify_requestor: z4.boolean().optional().default(false),
1100
+ notify_responsible: z4.boolean().optional().default(false)
1101
+ }),
1102
+ payload,
1103
+ action
1104
+ );
1105
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1106
+ const created = await client.createTicket(
1107
+ parsed.app_id,
1108
+ parsed.ticket,
1109
+ parsed.notify_requestor,
1110
+ parsed.notify_responsible
1111
+ );
1112
+ return toSuccessResponse(created, response_format);
1113
+ }
1114
+ case "update_ticket": {
1115
+ const parsed = parsePayload(
1116
+ z4.object({ app_id: TeamDynamixAppIdSchema, patch: TicketPatchSchema }),
1117
+ payload,
1118
+ action
1119
+ );
1120
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1121
+ const updated = await client.updateTicket(
1122
+ parsed.app_id,
1123
+ parsed.patch.TicketID,
1124
+ parsed.patch.Attributes,
1125
+ parsed.patch.NotifyRequestor ?? false,
1126
+ parsed.patch.NotifyResponsible ?? false,
1127
+ parsed.patch.Comments ?? "",
1128
+ parsed.patch.IsPrivate ?? false
1129
+ );
1130
+ return toSuccessResponse(updated, response_format);
1131
+ }
1132
+ case "add_ticket_comment": {
1133
+ const parsed = parsePayload(
1134
+ z4.object({ app_id: TeamDynamixAppIdSchema, comment: TicketCommentSchema }),
1135
+ payload,
1136
+ action
1137
+ );
1138
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1139
+ const entry = await client.addTicketComment(
1140
+ parsed.app_id,
1141
+ parsed.comment.TicketID,
1142
+ parsed.comment.Body,
1143
+ parsed.comment.IsPrivate ?? false,
1144
+ parsed.comment.NotifyRequestor ?? false,
1145
+ parsed.comment.NotifyResponsible ?? false
1146
+ );
1147
+ return toSuccessResponse(entry, response_format);
1148
+ }
1149
+ case "get_ticket_feed": {
1150
+ const parsed = parsePayload(
1151
+ z4.object({ app_id: TeamDynamixAppIdSchema, ticket_id: z4.number().int().positive() }),
1152
+ payload,
1153
+ action
1154
+ );
1155
+ const feed = await client.getTicketFeed(parsed.app_id, parsed.ticket_id);
1156
+ return toSuccessResponse(
1157
+ { appId: parsed.app_id, ticketId: parsed.ticket_id, entries: feed },
1158
+ response_format
1159
+ );
1160
+ }
1161
+ }
1162
+ } catch (error) {
1163
+ return toErrorResponse(error);
1164
+ }
1165
+ }
1166
+ );
1167
+ }
1168
+ function registerTeamDynamixTicketRelationshipsGateway(server2) {
1169
+ server2.registerTool(
1170
+ teamdynamixTicketRelationshipsTool,
1171
+ {
1172
+ title: "TeamDynamix Ticket Relationships Gateway",
1173
+ description: "Gateway for ticket tasks, linked assets, and linked contacts operations.",
1174
+ inputSchema: {
1175
+ action: teamdynamixTicketRelationshipActionSchema,
1176
+ payload: gatewayPayloadSchema,
1177
+ response_format: TeamDynamixResponseFormatSchema
1178
+ },
1179
+ annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: true, openWorldHint: true }
1180
+ },
1181
+ async ({ action, payload, response_format }) => {
1182
+ try {
1183
+ const client = createConfiguredTeamDynamixClient();
1184
+ switch (action) {
1185
+ case "get_ticket_tasks": {
1186
+ const parsed = parsePayload(
1187
+ z4.object({ app_id: TeamDynamixAppIdSchema, ticket_id: z4.number().int().positive() }),
1188
+ payload,
1189
+ action
1190
+ );
1191
+ const tasks = await client.getTicketTasks(parsed.app_id, parsed.ticket_id);
1192
+ return toSuccessResponse(
1193
+ { appId: parsed.app_id, ticketId: parsed.ticket_id, count: tasks.length, tasks },
1194
+ response_format
1195
+ );
1196
+ }
1197
+ case "create_ticket_task": {
1198
+ const parsed = parsePayload(
1199
+ z4.object({ app_id: TeamDynamixAppIdSchema, task: TicketTaskCreateSchema }),
1200
+ payload,
1201
+ action
1202
+ );
1203
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1204
+ const created = await client.createTicketTask(
1205
+ parsed.app_id,
1206
+ parsed.task.TicketID,
1207
+ parsed.task
1208
+ );
1209
+ return toSuccessResponse(created, response_format);
1210
+ }
1211
+ case "list_ticket_assets": {
1212
+ const parsed = parsePayload(
1213
+ z4.object({ app_id: TeamDynamixAppIdSchema, ticket_id: z4.number().int().positive() }),
1214
+ payload,
1215
+ action
1216
+ );
1217
+ const assets = await client.listTicketAssets(parsed.app_id, parsed.ticket_id);
1218
+ return toSuccessResponse(
1219
+ { appId: parsed.app_id, ticketId: parsed.ticket_id, count: assets.length, assets },
1220
+ response_format
1221
+ );
1222
+ }
1223
+ case "add_ticket_asset": {
1224
+ const parsed = parsePayload(
1225
+ z4.object({
1226
+ app_id: TeamDynamixAppIdSchema,
1227
+ ticket_id: z4.number().int().positive(),
1228
+ asset_id: z4.number().int().positive()
1229
+ }),
1230
+ payload,
1231
+ action
1232
+ );
1233
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1234
+ const result = await client.addTicketAsset(parsed.app_id, parsed.ticket_id, parsed.asset_id);
1235
+ return toSuccessResponse(result, response_format);
1236
+ }
1237
+ case "remove_ticket_asset": {
1238
+ const parsed = parsePayload(
1239
+ z4.object({
1240
+ app_id: TeamDynamixAppIdSchema,
1241
+ ticket_id: z4.number().int().positive(),
1242
+ asset_id: z4.number().int().positive(),
1243
+ confirm: z4.literal(true)
1244
+ }),
1245
+ payload,
1246
+ action
1247
+ );
1248
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1249
+ await client.removeTicketAsset(parsed.app_id, parsed.ticket_id, parsed.asset_id);
1250
+ return toSuccessResponse(
1251
+ {
1252
+ appId: parsed.app_id,
1253
+ ticketId: parsed.ticket_id,
1254
+ assetId: parsed.asset_id,
1255
+ unlinked: true
1256
+ },
1257
+ response_format
1258
+ );
1259
+ }
1260
+ case "get_ticket_contacts": {
1261
+ const parsed = parsePayload(
1262
+ z4.object({ app_id: TeamDynamixAppIdSchema, ticket_id: z4.number().int().positive() }),
1263
+ payload,
1264
+ action
1265
+ );
1266
+ const contacts = await client.getTicketContacts(parsed.app_id, parsed.ticket_id);
1267
+ return toSuccessResponse(
1268
+ { appId: parsed.app_id, ticketId: parsed.ticket_id, count: contacts.length, contacts },
1269
+ response_format
1270
+ );
1271
+ }
1272
+ case "add_ticket_contact": {
1273
+ const parsed = parsePayload(
1274
+ z4.object({
1275
+ app_id: TeamDynamixAppIdSchema,
1276
+ ticket_id: z4.number().int().positive(),
1277
+ contact_uid: TeamDynamixGuidSchema
1278
+ }),
1279
+ payload,
1280
+ action
1281
+ );
1282
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1283
+ const result = await client.addTicketContact(parsed.app_id, parsed.ticket_id, parsed.contact_uid);
1284
+ return toSuccessResponse(result, response_format);
1285
+ }
1286
+ case "remove_ticket_contact": {
1287
+ const parsed = parsePayload(
1288
+ z4.object({
1289
+ app_id: TeamDynamixAppIdSchema,
1290
+ ticket_id: z4.number().int().positive(),
1291
+ contact_uid: TeamDynamixGuidSchema,
1292
+ confirm: z4.literal(true)
1293
+ }),
1294
+ payload,
1295
+ action
1296
+ );
1297
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1298
+ await client.removeTicketContact(parsed.app_id, parsed.ticket_id, parsed.contact_uid);
1299
+ return toSuccessResponse(
1300
+ {
1301
+ appId: parsed.app_id,
1302
+ ticketId: parsed.ticket_id,
1303
+ contactUid: parsed.contact_uid,
1304
+ removed: true
1305
+ },
1306
+ response_format
1307
+ );
1308
+ }
1309
+ }
1310
+ } catch (error) {
1311
+ return toErrorResponse(error);
1312
+ }
1313
+ }
1314
+ );
1315
+ }
1316
+ function registerTeamDynamixPeopleGateway(server2) {
1317
+ server2.registerTool(
1318
+ teamdynamixPeopleTool,
1319
+ {
1320
+ title: "TeamDynamix People Gateway",
1321
+ description: "Gateway for user and group retrieval/search operations.",
1322
+ inputSchema: {
1323
+ action: teamdynamixPeopleActionSchema,
1324
+ payload: gatewayPayloadSchema,
1325
+ response_format: TeamDynamixResponseFormatSchema
1326
+ },
1327
+ annotations: { readOnlyHint: true, idempotentHint: false, destructiveHint: false, openWorldHint: true }
1328
+ },
1329
+ async ({ action, payload, response_format }) => {
1330
+ try {
1331
+ const client = createConfiguredTeamDynamixClient();
1332
+ switch (action) {
1333
+ case "get_user": {
1334
+ const parsed = parsePayload(z4.object({ uid: TeamDynamixGuidSchema }), payload, action);
1335
+ const user = await client.getUser(parsed.uid);
1336
+ return toSuccessResponse(user, response_format);
1337
+ }
1338
+ case "search_users": {
1339
+ const parsed = parsePayload(z4.object({ search: UserSearchSchema }), payload, action);
1340
+ const users = await client.searchUsers(parsed.search);
1341
+ return toSuccessResponse({ count: users.length, users }, response_format);
1342
+ }
1343
+ case "get_group": {
1344
+ const parsed = parsePayload(z4.object({ group_id: z4.number().int().positive() }), payload, action);
1345
+ const group = await client.getGroup(parsed.group_id);
1346
+ return toSuccessResponse(group, response_format);
1347
+ }
1348
+ case "search_groups": {
1349
+ const parsed = parsePayload(z4.object({ search: GroupSearchSchema }), payload, action);
1350
+ const groups = await client.searchGroups(parsed.search);
1351
+ return toSuccessResponse({ count: groups.length, groups }, response_format);
1352
+ }
1353
+ case "get_group_members": {
1354
+ const parsed = parsePayload(z4.object({ group_id: z4.number().int().positive() }), payload, action);
1355
+ const members = await client.getGroupMembers(parsed.group_id);
1356
+ return toSuccessResponse({ groupId: parsed.group_id, count: members.length, members }, response_format);
1357
+ }
1358
+ }
1359
+ } catch (error) {
1360
+ return toErrorResponse(error);
1361
+ }
1362
+ }
1363
+ );
1364
+ }
1365
+ function registerTeamDynamixKnowledgeBaseGateway(server2) {
1366
+ server2.registerTool(
1367
+ teamdynamixKnowledgeBaseTool,
1368
+ {
1369
+ title: "TeamDynamix Knowledge Base Gateway",
1370
+ description: "Gateway for TeamDynamix Knowledge Base retrieval, search, and write operations.",
1371
+ inputSchema: {
1372
+ action: teamdynamixKnowledgeBaseActionSchema,
1373
+ payload: gatewayPayloadSchema,
1374
+ response_format: TeamDynamixResponseFormatSchema
1375
+ },
1376
+ annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: true, openWorldHint: true }
1377
+ },
1378
+ async ({ action, payload, response_format }) => {
1379
+ try {
1380
+ const client = createConfiguredTeamDynamixClient();
1381
+ switch (action) {
1382
+ case "get_kb_article": {
1383
+ const parsed = parsePayload(
1384
+ z4.object({ app_id: TeamDynamixAppIdSchema, article_id: z4.number().int().positive() }),
1385
+ payload,
1386
+ action
1387
+ );
1388
+ const article = await client.getKbArticle(parsed.app_id, parsed.article_id);
1389
+ return toSuccessResponse(article, response_format);
1390
+ }
1391
+ case "search_kb_articles": {
1392
+ const parsed = parsePayload(
1393
+ z4.object({ app_id: TeamDynamixAppIdSchema, search: KbArticleSearchSchema }),
1394
+ payload,
1395
+ action
1396
+ );
1397
+ const articles = await client.searchKbArticles(parsed.app_id, parsed.search);
1398
+ return toSuccessResponse({ appId: parsed.app_id, count: articles.length, articles }, response_format);
1399
+ }
1400
+ case "list_kb_categories": {
1401
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1402
+ const categories = await client.listKbCategories(parsed.app_id);
1403
+ return toSuccessResponse({ appId: parsed.app_id, count: categories.length, categories }, response_format);
1404
+ }
1405
+ case "create_kb_article": {
1406
+ const parsed = parsePayload(
1407
+ z4.object({ app_id: TeamDynamixAppIdSchema, article: KbArticleCreateSchema }),
1408
+ payload,
1409
+ action
1410
+ );
1411
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1412
+ const created = await client.createKbArticle(parsed.app_id, parsed.article);
1413
+ return toSuccessResponse(created, response_format);
1414
+ }
1415
+ case "update_kb_article": {
1416
+ const parsed = parsePayload(
1417
+ z4.object({ app_id: TeamDynamixAppIdSchema, article: KbArticleUpdateSchema }),
1418
+ payload,
1419
+ action
1420
+ );
1421
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1422
+ const { ArticleID, ...body } = parsed.article;
1423
+ const updated = await client.updateKbArticle(parsed.app_id, ArticleID, body);
1424
+ return toSuccessResponse(updated, response_format);
1425
+ }
1426
+ }
1427
+ } catch (error) {
1428
+ return toErrorResponse(error);
1429
+ }
1430
+ }
1431
+ );
1432
+ }
1433
+ function registerTeamDynamixAssetsGateway(server2) {
1434
+ server2.registerTool(
1435
+ teamdynamixAssetsTool,
1436
+ {
1437
+ title: "TeamDynamix Assets Gateway",
1438
+ description: "Gateway for TeamDynamix asset retrieval, search, and lookup enumerations.",
1439
+ inputSchema: {
1440
+ action: teamdynamixAssetsActionSchema,
1441
+ payload: gatewayPayloadSchema,
1442
+ response_format: TeamDynamixResponseFormatSchema
1443
+ },
1444
+ annotations: { readOnlyHint: true, idempotentHint: false, destructiveHint: false, openWorldHint: true }
1445
+ },
1446
+ async ({ action, payload, response_format }) => {
1447
+ try {
1448
+ const client = createConfiguredTeamDynamixClient();
1449
+ switch (action) {
1450
+ case "get_asset": {
1451
+ const parsed = parsePayload(
1452
+ z4.object({ app_id: TeamDynamixAppIdSchema, asset_id: z4.number().int().positive() }),
1453
+ payload,
1454
+ action
1455
+ );
1456
+ const asset = await client.getAsset(parsed.app_id, parsed.asset_id);
1457
+ return toSuccessResponse(asset, response_format);
1458
+ }
1459
+ case "search_assets": {
1460
+ const parsed = parsePayload(
1461
+ z4.object({ app_id: TeamDynamixAppIdSchema, search: AssetSearchSchema }),
1462
+ payload,
1463
+ action
1464
+ );
1465
+ const assets = await client.searchAssets(parsed.app_id, parsed.search);
1466
+ return toSuccessResponse({ appId: parsed.app_id, count: assets.length, assets }, response_format);
1467
+ }
1468
+ case "list_asset_statuses": {
1469
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1470
+ const statuses = await client.listAssetStatuses(parsed.app_id);
1471
+ return toSuccessResponse({ appId: parsed.app_id, count: statuses.length, statuses }, response_format);
1472
+ }
1473
+ case "list_product_models": {
1474
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1475
+ const models = await client.listProductModels(parsed.app_id);
1476
+ return toSuccessResponse({ appId: parsed.app_id, count: models.length, models }, response_format);
1477
+ }
1478
+ }
1479
+ } catch (error) {
1480
+ return toErrorResponse(error);
1481
+ }
1482
+ }
1483
+ );
1484
+ }
1485
+ function registerTeamDynamixCmdbGateway(server2) {
1486
+ server2.registerTool(
1487
+ teamdynamixCmdbTool,
1488
+ {
1489
+ title: "TeamDynamix CMDB Gateway",
1490
+ description: "Gateway for CI/CMDB retrieval, search, types, relationship types, and vendors.",
1491
+ inputSchema: {
1492
+ action: teamdynamixCmdbActionSchema,
1493
+ payload: gatewayPayloadSchema,
1494
+ response_format: TeamDynamixResponseFormatSchema
1495
+ },
1496
+ annotations: { readOnlyHint: true, idempotentHint: false, destructiveHint: false, openWorldHint: true }
1497
+ },
1498
+ async ({ action, payload, response_format }) => {
1499
+ try {
1500
+ const client = createConfiguredTeamDynamixClient();
1501
+ switch (action) {
1502
+ case "get_ci": {
1503
+ const parsed = parsePayload(
1504
+ z4.object({ app_id: TeamDynamixAppIdSchema, ci_id: z4.number().int().positive() }),
1505
+ payload,
1506
+ action
1507
+ );
1508
+ const ci = await client.getConfigurationItem(parsed.app_id, parsed.ci_id);
1509
+ return toSuccessResponse(ci, response_format);
1510
+ }
1511
+ case "search_cis": {
1512
+ const parsed = parsePayload(
1513
+ z4.object({ app_id: TeamDynamixAppIdSchema, search: CiSearchSchema }),
1514
+ payload,
1515
+ action
1516
+ );
1517
+ const results = await client.searchConfigurationItems(
1518
+ parsed.app_id,
1519
+ parsed.search
1520
+ );
1521
+ return toSuccessResponse({ appId: parsed.app_id, count: results.length, results }, response_format);
1522
+ }
1523
+ case "list_ci_types": {
1524
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1525
+ const types = await client.listCiTypes(parsed.app_id);
1526
+ return toSuccessResponse({ appId: parsed.app_id, count: types.length, types }, response_format);
1527
+ }
1528
+ case "list_ci_relationship_types": {
1529
+ const types = await client.listCiRelationshipTypes();
1530
+ return toSuccessResponse({ count: types.length, types }, response_format);
1531
+ }
1532
+ case "list_vendors": {
1533
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1534
+ const vendors = await client.listVendors(parsed.app_id);
1535
+ return toSuccessResponse({ appId: parsed.app_id, count: vendors.length, vendors }, response_format);
1536
+ }
1537
+ }
1538
+ } catch (error) {
1539
+ return toErrorResponse(error);
1540
+ }
1541
+ }
1542
+ );
1543
+ }
1544
+ function registerTeamDynamixServicesGateway(server2) {
1545
+ server2.registerTool(
1546
+ teamdynamixServicesTool,
1547
+ {
1548
+ title: "TeamDynamix Services Gateway",
1549
+ description: "Gateway for TeamDynamix service catalog list/get/search/category operations.",
1550
+ inputSchema: {
1551
+ action: teamdynamixServicesActionSchema,
1552
+ payload: gatewayPayloadSchema,
1553
+ response_format: TeamDynamixResponseFormatSchema
1554
+ },
1555
+ annotations: { readOnlyHint: true, idempotentHint: false, destructiveHint: false, openWorldHint: true }
1556
+ },
1557
+ async ({ action, payload, response_format }) => {
1558
+ try {
1559
+ const client = createConfiguredTeamDynamixClient();
1560
+ switch (action) {
1561
+ case "list_services": {
1562
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1563
+ const services = await client.listServiceCatalog(parsed.app_id);
1564
+ return toSuccessResponse({ appId: parsed.app_id, count: services.length, services }, response_format);
1565
+ }
1566
+ case "get_service": {
1567
+ const parsed = parsePayload(
1568
+ z4.object({ app_id: TeamDynamixAppIdSchema, service_id: z4.number().int().positive() }),
1569
+ payload,
1570
+ action
1571
+ );
1572
+ const service = await client.getService(parsed.app_id, parsed.service_id);
1573
+ return toSuccessResponse(service, response_format);
1574
+ }
1575
+ case "search_services": {
1576
+ const parsed = parsePayload(
1577
+ z4.object({ app_id: TeamDynamixAppIdSchema, search: ServiceSearchSchema }),
1578
+ payload,
1579
+ action
1580
+ );
1581
+ const services = await client.searchServices(parsed.app_id, parsed.search);
1582
+ return toSuccessResponse({ appId: parsed.app_id, count: services.length, services }, response_format);
1583
+ }
1584
+ case "list_service_categories": {
1585
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema }), payload, action);
1586
+ const categories = await client.listServiceCategories(parsed.app_id);
1587
+ return toSuccessResponse({ appId: parsed.app_id, count: categories.length, categories }, response_format);
1588
+ }
1589
+ }
1590
+ } catch (error) {
1591
+ return toErrorResponse(error);
1592
+ }
1593
+ }
1594
+ );
1595
+ }
1596
+ function registerTeamDynamixProjectsGateway(server2) {
1597
+ server2.registerTool(
1598
+ teamdynamixProjectsTool,
1599
+ {
1600
+ title: "TeamDynamix Projects Gateway",
1601
+ description: "Gateway for TeamDynamix projects retrieval/search and project issue/risk operations.",
1602
+ inputSchema: {
1603
+ action: teamdynamixProjectsActionSchema,
1604
+ payload: gatewayPayloadSchema,
1605
+ response_format: TeamDynamixResponseFormatSchema
1606
+ },
1607
+ annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: true, openWorldHint: true }
1608
+ },
1609
+ async ({ action, payload, response_format }) => {
1610
+ try {
1611
+ const client = createConfiguredTeamDynamixClient();
1612
+ switch (action) {
1613
+ case "get_project": {
1614
+ const parsed = parsePayload(z4.object({ project_id: z4.number().int().positive() }), payload, action);
1615
+ const project = await client.getProject(parsed.project_id);
1616
+ return toSuccessResponse(project, response_format);
1617
+ }
1618
+ case "search_projects": {
1619
+ const parsed = parsePayload(z4.object({ search: ProjectSearchSchema }), payload, action);
1620
+ const projects = await client.searchProjects(parsed.search);
1621
+ return toSuccessResponse({ count: projects.length, projects }, response_format);
1622
+ }
1623
+ case "list_project_types": {
1624
+ const types = await client.listProjectTypes();
1625
+ return toSuccessResponse({ count: types.length, types }, response_format);
1626
+ }
1627
+ case "get_project_plans": {
1628
+ const parsed = parsePayload(z4.object({ project_id: z4.number().int().positive() }), payload, action);
1629
+ const plans = await client.getProjectPlans(parsed.project_id);
1630
+ return toSuccessResponse({ projectId: parsed.project_id, count: plans.length, plans }, response_format);
1631
+ }
1632
+ case "get_project_issues": {
1633
+ const parsed = parsePayload(z4.object({ project_id: z4.number().int().positive() }), payload, action);
1634
+ const issues = await client.getProjectIssues(parsed.project_id);
1635
+ return toSuccessResponse({ projectId: parsed.project_id, count: issues.length, issues }, response_format);
1636
+ }
1637
+ case "get_project_risks": {
1638
+ const parsed = parsePayload(z4.object({ project_id: z4.number().int().positive() }), payload, action);
1639
+ const risks = await client.getProjectRisks(parsed.project_id);
1640
+ return toSuccessResponse({ projectId: parsed.project_id, count: risks.length, risks }, response_format);
1641
+ }
1642
+ case "create_project_issue": {
1643
+ const parsed = parsePayload(
1644
+ z4.object({ project_id: z4.number().int().positive(), issue: ProjectIssueCreateSchema }),
1645
+ payload,
1646
+ action
1647
+ );
1648
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1649
+ const created = await client.createProjectIssue(parsed.project_id, parsed.issue);
1650
+ return toSuccessResponse(created, response_format);
1651
+ }
1652
+ case "create_project_risk": {
1653
+ const parsed = parsePayload(
1654
+ z4.object({ project_id: z4.number().int().positive(), risk: ProjectRiskCreateSchema }),
1655
+ payload,
1656
+ action
1657
+ );
1658
+ assertWriteToolsEnabled(getTeamDynamixConfig());
1659
+ const created = await client.createProjectRisk(parsed.project_id, parsed.risk);
1660
+ return toSuccessResponse(created, response_format);
1661
+ }
1662
+ }
1663
+ } catch (error) {
1664
+ return toErrorResponse(error);
1665
+ }
1666
+ }
1667
+ );
1668
+ }
1669
+ function registerTeamDynamixTimeGateway(server2) {
1670
+ server2.registerTool(
1671
+ teamdynamixTimeTool,
1672
+ {
1673
+ title: "TeamDynamix Time Gateway",
1674
+ description: "Gateway for TeamDynamix time type lookup and authenticated user time entries.",
1675
+ inputSchema: {
1676
+ action: teamdynamixTimeActionSchema,
1677
+ payload: gatewayPayloadSchema,
1678
+ response_format: TeamDynamixResponseFormatSchema
1679
+ },
1680
+ annotations: { readOnlyHint: true, idempotentHint: false, destructiveHint: false, openWorldHint: true }
1681
+ },
1682
+ async ({ action, payload, response_format }) => {
1683
+ try {
1684
+ const client = createConfiguredTeamDynamixClient();
1685
+ switch (action) {
1686
+ case "list_time_types": {
1687
+ const types = await client.listTimeTypes();
1688
+ return toSuccessResponse({ count: types.length, types }, response_format);
1689
+ }
1690
+ case "get_my_time_entries": {
1691
+ const parsed = parsePayload(z4.object({ query: TimeEntryQuerySchema }), payload, action);
1692
+ const entries = await client.getMyTimeEntries(parsed.query.StartDate, parsed.query.EndDate);
1693
+ return toSuccessResponse({ count: entries.length, entries }, response_format);
1694
+ }
1695
+ }
1696
+ } catch (error) {
1697
+ return toErrorResponse(error);
1698
+ }
1699
+ }
1700
+ );
1701
+ }
1702
+ function registerTeamDynamixReferenceDataGateway(server2) {
1703
+ server2.registerTool(
1704
+ teamdynamixReferenceDataTool,
1705
+ {
1706
+ title: "TeamDynamix Reference Data Gateway",
1707
+ description: "Gateway for TeamDynamix reference/enumeration lookups such as accounts, locations, roles, and custom attributes.",
1708
+ inputSchema: {
1709
+ action: teamdynamixReferenceDataActionSchema,
1710
+ payload: gatewayPayloadSchema,
1711
+ response_format: TeamDynamixResponseFormatSchema
1712
+ },
1713
+ annotations: { readOnlyHint: true, idempotentHint: false, destructiveHint: false, openWorldHint: true }
1714
+ },
1715
+ async ({ action, payload, response_format }) => {
1716
+ try {
1717
+ const client = createConfiguredTeamDynamixClient();
1718
+ switch (action) {
1719
+ case "list_accounts": {
1720
+ const parsed = parsePayload(z4.object({ app_id: TeamDynamixAppIdSchema.optional() }), payload, action);
1721
+ const accounts = await client.listAccounts(parsed.app_id);
1722
+ return toSuccessResponse({ count: accounts.length, accounts }, response_format);
1723
+ }
1724
+ case "get_account": {
1725
+ const parsed = parsePayload(z4.object({ account_id: z4.number().int().positive() }), payload, action);
1726
+ const account = await client.getAccount(parsed.account_id);
1727
+ return toSuccessResponse(account, response_format);
1728
+ }
1729
+ case "list_locations": {
1730
+ const locations = await client.listLocations();
1731
+ return toSuccessResponse({ count: locations.length, locations }, response_format);
1732
+ }
1733
+ case "list_functional_roles": {
1734
+ const roles = await client.listFunctionalRoles();
1735
+ return toSuccessResponse({ count: roles.length, roles }, response_format);
1736
+ }
1737
+ case "list_custom_attributes": {
1738
+ const parsed = parsePayload(
1739
+ z4.object({
1740
+ component_id: CustomAttributeComponentIdSchema,
1741
+ app_id: TeamDynamixAppIdSchema.optional(),
1742
+ associated_type_id: z4.number().int().positive().optional()
1743
+ }),
1744
+ payload,
1745
+ action
1746
+ );
1747
+ const attributes = await client.listCustomAttributes(
1748
+ parsed.component_id,
1749
+ parsed.app_id,
1750
+ parsed.associated_type_id
1751
+ );
1752
+ return toSuccessResponse(
1753
+ { componentId: parsed.component_id, count: attributes.length, attributes },
1754
+ response_format
1755
+ );
1756
+ }
1757
+ }
1758
+ } catch (error) {
1759
+ return toErrorResponse(error);
1760
+ }
1761
+ }
1762
+ );
1763
+ }
1764
+
1765
+ // src/resources/teamdynamix.resources.ts
1766
+ function stringify(data) {
1767
+ return JSON.stringify(data, null, 2);
1768
+ }
1769
+ function registerTeamDynamixResources(server2) {
1770
+ const resourceConfig = { list: void 0 };
1771
+ server2.registerResource(
1772
+ "teamdynamix_capabilities",
1773
+ new ResourceTemplate("teamdynamix://capabilities", resourceConfig),
1774
+ {
1775
+ title: "TeamDynamix Capabilities",
1776
+ description: "Read-only snapshot of the currently implemented TeamDynamix MCP surface.",
1777
+ mimeType: "application/json"
1778
+ },
1779
+ async (uri) => {
1780
+ const capabilities = {
1781
+ toolGroups: {
1782
+ discovery: [TEAMDYNAMIX_GATEWAY_SURFACE.discovery.tool],
1783
+ tickets: [TEAMDYNAMIX_GATEWAY_SURFACE.tickets.tool],
1784
+ ticket_relationships: [TEAMDYNAMIX_GATEWAY_SURFACE.ticketRelationships.tool],
1785
+ people: [TEAMDYNAMIX_GATEWAY_SURFACE.people.tool],
1786
+ knowledge_base: [TEAMDYNAMIX_GATEWAY_SURFACE.knowledgeBase.tool],
1787
+ assets: [TEAMDYNAMIX_GATEWAY_SURFACE.assets.tool],
1788
+ cmdb: [TEAMDYNAMIX_GATEWAY_SURFACE.cmdb.tool],
1789
+ services: [TEAMDYNAMIX_GATEWAY_SURFACE.services.tool],
1790
+ projects: [TEAMDYNAMIX_GATEWAY_SURFACE.projects.tool],
1791
+ time: [TEAMDYNAMIX_GATEWAY_SURFACE.time.tool],
1792
+ reference_data: [TEAMDYNAMIX_GATEWAY_SURFACE.referenceData.tool]
1793
+ },
1794
+ gatewayActions: {
1795
+ discovery: TEAMDYNAMIX_GATEWAY_SURFACE.discovery.actions,
1796
+ tickets: TEAMDYNAMIX_GATEWAY_SURFACE.tickets.actions,
1797
+ ticket_relationships: TEAMDYNAMIX_GATEWAY_SURFACE.ticketRelationships.actions,
1798
+ people: TEAMDYNAMIX_GATEWAY_SURFACE.people.actions,
1799
+ knowledge_base: TEAMDYNAMIX_GATEWAY_SURFACE.knowledgeBase.actions,
1800
+ assets: TEAMDYNAMIX_GATEWAY_SURFACE.assets.actions,
1801
+ cmdb: TEAMDYNAMIX_GATEWAY_SURFACE.cmdb.actions,
1802
+ services: TEAMDYNAMIX_GATEWAY_SURFACE.services.actions,
1803
+ projects: TEAMDYNAMIX_GATEWAY_SURFACE.projects.actions,
1804
+ time: TEAMDYNAMIX_GATEWAY_SURFACE.time.actions,
1805
+ reference_data: TEAMDYNAMIX_GATEWAY_SURFACE.referenceData.actions
1806
+ },
1807
+ implementedDomains: [
1808
+ "discovery",
1809
+ "tickets",
1810
+ "ticket_relationships",
1811
+ "people",
1812
+ "knowledge_base",
1813
+ "assets",
1814
+ "cmdb",
1815
+ "services",
1816
+ "projects",
1817
+ "time",
1818
+ "reference_data"
1819
+ ]
1820
+ };
1821
+ return {
1822
+ contents: [{ uri: uri.toString(), mimeType: "application/json", text: stringify(capabilities) }]
1823
+ };
1824
+ }
1825
+ );
1826
+ server2.registerResource(
1827
+ "teamdynamix_config",
1828
+ new ResourceTemplate("teamdynamix://config", resourceConfig),
1829
+ {
1830
+ title: "TeamDynamix Runtime Config",
1831
+ description: "Read-only sanitized TeamDynamix configuration and readiness metadata.",
1832
+ mimeType: "application/json"
1833
+ },
1834
+ async (uri) => {
1835
+ const runtimeConfig = getTeamDynamixConfig();
1836
+ const config = {
1837
+ status: getTeamDynamixConfigStatus(runtimeConfig),
1838
+ config: redactTeamDynamixConfig(runtimeConfig)
1839
+ };
1840
+ return {
1841
+ contents: [{ uri: uri.toString(), mimeType: "application/json", text: stringify(config) }]
1842
+ };
1843
+ }
1844
+ );
1845
+ }
1846
+
1847
+ // src/index.ts
1848
+ var server = new McpServer2({
1849
+ name: SERVER_NAME_OVERRIDE ?? SERVER_NAME,
1850
+ version: SERVER_VERSION_OVERRIDE ?? SERVER_VERSION
1851
+ });
1852
+ registerTeamDynamixDomainGatewayTools(server);
1853
+ registerTeamDynamixResources(server);
1854
+ async function main() {
1855
+ console.error(`[teamdynamix-mcp] log level: ${LOG_LEVEL}`);
1856
+ if (LOG_LEVEL === "debug") {
1857
+ const config = getTeamDynamixConfig();
1858
+ console.error(`[teamdynamix-mcp] sanitized config: ${JSON.stringify(redactTeamDynamixConfig(config))}`);
1859
+ }
1860
+ const transport = new StdioServerTransport();
1861
+ await server.connect(transport);
1862
+ }
1863
+ main().catch((error) => {
1864
+ const message = error instanceof Error ? error.message : String(error);
1865
+ console.error(`Server startup failed: ${message}`);
1866
+ process.exit(1);
1867
+ });
1868
+ //# sourceMappingURL=index.js.map