@selfagency/teamdynamix-mcp 0.1.1 → 0.2.1
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 +11 -1
- package/index.js +454 -427
- package/index.js.map +1 -1
- package/package.json +6 -2
package/index.js
CHANGED
|
@@ -10,12 +10,11 @@ import { z } from "zod";
|
|
|
10
10
|
// src/constants.ts
|
|
11
11
|
var SERVER_NAME = "teamdynamix-mcp";
|
|
12
12
|
var SERVER_VERSION = "0.2.0";
|
|
13
|
+
var CHARACTER_LIMIT = 25e3;
|
|
13
14
|
var TEAMDYNAMIX_TOOL_PREFIX = "teamdynamix";
|
|
14
15
|
var TEAMDYNAMIX_DEFAULT_TIMEOUT_MS = 3e4;
|
|
15
16
|
var TEAMDYNAMIX_DEFAULT_MAX_RETRIES = 2;
|
|
16
17
|
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
18
|
|
|
20
19
|
// src/config.ts
|
|
21
20
|
var SERVER_NAME_OVERRIDE = process.env["MCP_SERVER_NAME"]?.trim() || void 0;
|
|
@@ -92,7 +91,8 @@ function getTeamDynamixConfig() {
|
|
|
92
91
|
timeoutMs: normalizeNumberWithDefault(process.env["TEAMDYNAMIX_TIMEOUT_MS"], TEAMDYNAMIX_DEFAULT_TIMEOUT_MS, 1e3),
|
|
93
92
|
maxRetries,
|
|
94
93
|
enableWriteTools: normalizeBoolean(process.env["TEAMDYNAMIX_ENABLE_WRITE_TOOLS"], false),
|
|
95
|
-
enableAdminTools: normalizeBoolean(process.env["TEAMDYNAMIX_ENABLE_ADMIN_TOOLS"], false)
|
|
94
|
+
enableAdminTools: normalizeBoolean(process.env["TEAMDYNAMIX_ENABLE_ADMIN_TOOLS"], false),
|
|
95
|
+
enableDeleteTools: normalizeBoolean(process.env["TEAMDYNAMIX_ENABLE_DELETE_TOOLS"], false)
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
function getTeamDynamixConfigStatus(config = getTeamDynamixConfig()) {
|
|
@@ -118,26 +118,6 @@ function getTeamDynamixConfigStatus(config = getTeamDynamixConfig()) {
|
|
|
118
118
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
119
119
|
|
|
120
120
|
// src/services/teamdynamix/core.service.ts
|
|
121
|
-
function parseRateLimit(headers, nowMs = Date.now()) {
|
|
122
|
-
const limitRaw = headers.get("X-RateLimit-Limit");
|
|
123
|
-
const remainingRaw = headers.get("X-RateLimit-Remaining");
|
|
124
|
-
const resetRaw = headers.get("X-RateLimit-Reset");
|
|
125
|
-
const resetTime = resetRaw ? Date.parse(resetRaw) : Number.NaN;
|
|
126
|
-
const computedWaitMs = Number.isFinite(resetTime) ? resetTime - nowMs : TEAMDYNAMIX_MIN_RATE_LIMIT_WAIT_MS;
|
|
127
|
-
const waitMs = Math.min(
|
|
128
|
-
Math.max(
|
|
129
|
-
Number.isFinite(computedWaitMs) ? computedWaitMs : TEAMDYNAMIX_MIN_RATE_LIMIT_WAIT_MS,
|
|
130
|
-
TEAMDYNAMIX_MIN_RATE_LIMIT_WAIT_MS
|
|
131
|
-
),
|
|
132
|
-
TEAMDYNAMIX_MAX_RATE_LIMIT_WAIT_MS
|
|
133
|
-
);
|
|
134
|
-
return {
|
|
135
|
-
limit: limitRaw ? Number.parseInt(limitRaw, 10) : null,
|
|
136
|
-
remaining: remainingRaw ? Number.parseInt(remainingRaw, 10) : null,
|
|
137
|
-
resetAt: Number.isFinite(resetTime) ? new Date(resetTime).toISOString() : null,
|
|
138
|
-
waitMs
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
121
|
function redactTeamDynamixConfig(config) {
|
|
142
122
|
return {
|
|
143
123
|
baseUrl: config.baseUrl ?? null,
|
|
@@ -155,39 +135,6 @@ function redactTeamDynamixConfig(config) {
|
|
|
155
135
|
enableAdminTools: config.enableAdminTools
|
|
156
136
|
};
|
|
157
137
|
}
|
|
158
|
-
function decodeJwtExpiryEpochSeconds(token) {
|
|
159
|
-
const segments = token.split(".");
|
|
160
|
-
const payload = segments[1];
|
|
161
|
-
if (!payload) {
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
try {
|
|
165
|
-
const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
166
|
-
const padding = normalized.length % 4 === 0 ? "" : "=".repeat(4 - normalized.length % 4);
|
|
167
|
-
const decoded = Buffer.from(`${normalized}${padding}`, "base64").toString("utf8");
|
|
168
|
-
const parsed = JSON.parse(decoded);
|
|
169
|
-
return typeof parsed.exp === "number" ? parsed.exp : null;
|
|
170
|
-
} catch {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
function extractAuthToken(payload) {
|
|
175
|
-
if (typeof payload === "string" && payload.trim()) {
|
|
176
|
-
return payload.trim();
|
|
177
|
-
}
|
|
178
|
-
if (!payload || typeof payload !== "object") {
|
|
179
|
-
throw new Error("Unable to extract bearer token from TeamDynamix authentication response.");
|
|
180
|
-
}
|
|
181
|
-
const record = payload;
|
|
182
|
-
const candidateKeys = ["token", "Token", "accessToken", "AccessToken", "bearerToken", "TokenText", "value"];
|
|
183
|
-
for (const key of candidateKeys) {
|
|
184
|
-
const candidate = record[key];
|
|
185
|
-
if (typeof candidate === "string" && candidate.trim()) {
|
|
186
|
-
return candidate.trim();
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
throw new Error("Unable to extract bearer token from TeamDynamix authentication response.");
|
|
190
|
-
}
|
|
191
138
|
|
|
192
139
|
// src/tools/teamdynamix.domain-gateways.tools.ts
|
|
193
140
|
import { z as z4 } from "zod";
|
|
@@ -397,441 +344,457 @@ var TeamDynamixUserSchema = z3.object({
|
|
|
397
344
|
var TeamDynamixListResponseSchema = z3.array(z3.record(z3.string(), z3.unknown())).describe("Array of TeamDynamix entities");
|
|
398
345
|
var TeamDynamixSingleResponseSchema = z3.record(z3.string(), z3.unknown()).describe("Single TeamDynamix entity response");
|
|
399
346
|
|
|
400
|
-
// src/
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
347
|
+
// src/client/sdk-client.factory.ts
|
|
348
|
+
import { createTeamDynamixClient, loginWithPassword, loginWithServiceAccount } from "@selfagency/teamdynamix-ts";
|
|
349
|
+
function createTokenProvider(config, tenant) {
|
|
350
|
+
if (config.authMode === "admin") {
|
|
351
|
+
if (!config.beid || !config.webServicesKey) {
|
|
352
|
+
throw new Error("BEID and WebServicesKey are required for admin authentication");
|
|
353
|
+
}
|
|
354
|
+
return loginWithServiceAccount({
|
|
355
|
+
tenant,
|
|
356
|
+
beid: config.beid,
|
|
357
|
+
webServicesKey: config.webServicesKey
|
|
358
|
+
});
|
|
406
359
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
360
|
+
if (!config.username || !config.password) {
|
|
361
|
+
throw new Error("Username and password are required for standard authentication");
|
|
362
|
+
}
|
|
363
|
+
return loginWithPassword({
|
|
364
|
+
tenant,
|
|
365
|
+
username: config.username,
|
|
366
|
+
password: config.password
|
|
411
367
|
});
|
|
412
368
|
}
|
|
413
|
-
function
|
|
414
|
-
|
|
369
|
+
function extractTenant(baseUrl) {
|
|
370
|
+
const normalized = baseUrl.replace(/\/+$/, "").replace(/\/TDWebApi$/, "");
|
|
371
|
+
const withoutProtocol = normalized.replace(/^https?:\/\//, "");
|
|
372
|
+
const parts = withoutProtocol.split("/");
|
|
373
|
+
return parts[0] ?? "";
|
|
415
374
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const now = Date.now();
|
|
427
|
-
const elapsed = now - this.lastRefillMs;
|
|
428
|
-
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRatePerMs);
|
|
429
|
-
this.lastRefillMs = now;
|
|
430
|
-
}
|
|
431
|
-
async acquire() {
|
|
432
|
-
this.refill();
|
|
433
|
-
if (this.tokens >= 1) {
|
|
434
|
-
this.tokens -= 1;
|
|
435
|
-
return;
|
|
375
|
+
function buildSdkConfig(config, tenant, tokenProvider) {
|
|
376
|
+
return {
|
|
377
|
+
tenant,
|
|
378
|
+
tokenProvider,
|
|
379
|
+
environment: config.baseUrl?.includes("sandbox") ? "sandbox" : "production",
|
|
380
|
+
baseUrl: config.baseUrl,
|
|
381
|
+
timeoutMs: config.timeoutMs,
|
|
382
|
+
runtimeValidationMode: config.enableAdminTools ? "fail-closed" : "fail-open",
|
|
383
|
+
retryPolicy: {
|
|
384
|
+
maxRetries: config.maxRetries
|
|
436
385
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
async function createMcpSdkClient(config) {
|
|
389
|
+
const tenant = extractTenant(config.baseUrl ?? "");
|
|
390
|
+
const tokenProvider = createTokenProvider(config, tenant);
|
|
391
|
+
const sdkConfig = buildSdkConfig(config, tenant, tokenProvider);
|
|
392
|
+
const { client } = await createTeamDynamixClient(sdkConfig);
|
|
393
|
+
return client;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/services/teamdynamix/client.factory.ts
|
|
397
|
+
var UnifiedTeamDynamixClient = class {
|
|
398
|
+
constructor(config) {
|
|
445
399
|
this.config = config;
|
|
446
|
-
this.rateLimiter = rateLimiter;
|
|
447
|
-
}
|
|
448
|
-
cachedToken = null;
|
|
449
|
-
async getCurrentUser() {
|
|
450
|
-
return await this.requestJson("/api/auth/getuser");
|
|
451
400
|
}
|
|
452
|
-
|
|
453
|
-
|
|
401
|
+
sdkPromise = null;
|
|
402
|
+
sdk() {
|
|
403
|
+
this.sdkPromise ??= createMcpSdkClient(this.config);
|
|
404
|
+
return this.sdkPromise;
|
|
454
405
|
}
|
|
455
|
-
//
|
|
456
|
-
// Discovery
|
|
457
|
-
//
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
return await this.requestJson(path);
|
|
406
|
+
//
|
|
407
|
+
// Discovery
|
|
408
|
+
//
|
|
409
|
+
getCurrentUser() {
|
|
410
|
+
return this.sdk().then((s) => s.discovery.authGetuser());
|
|
461
411
|
}
|
|
462
|
-
|
|
463
|
-
return
|
|
412
|
+
listApplications() {
|
|
413
|
+
return this.sdk().then((s) => s.discovery.applications());
|
|
464
414
|
}
|
|
465
|
-
|
|
466
|
-
|
|
415
|
+
//
|
|
416
|
+
// Tickets
|
|
417
|
+
//
|
|
418
|
+
getTicket(appId, ticketId) {
|
|
419
|
+
return this.sdk().then((s) => s.tickets.appIdTicketsId({ params: { path: { appId, id: ticketId } } }));
|
|
467
420
|
}
|
|
468
|
-
|
|
469
|
-
return
|
|
421
|
+
searchTickets(appId, _body) {
|
|
422
|
+
return this.sdk().then((s) => s.tickets.appIdTicketsFeed({ params: { path: { appId } } }));
|
|
470
423
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
if (appId !== void 0) qs.set("appId", String(appId));
|
|
474
|
-
if (associatedTypeId !== void 0) qs.set("associatedTypeId", String(associatedTypeId));
|
|
475
|
-
return await this.requestJson(`/api/attributes/custom?${qs}`);
|
|
424
|
+
listTicketTypes(appId) {
|
|
425
|
+
return this.sdk().then((s) => s.tickets.appIdTicketsTypes({ params: { path: { appId } } }));
|
|
476
426
|
}
|
|
477
|
-
|
|
478
|
-
return
|
|
427
|
+
listTicketPriorities(appId) {
|
|
428
|
+
return this.sdk().then((s) => s.tickets.appIdTicketsPriorities({ params: { path: { appId } } }));
|
|
479
429
|
}
|
|
480
|
-
|
|
481
|
-
return
|
|
430
|
+
listTicketUrgencies(appId) {
|
|
431
|
+
return this.sdk().then((s) => s.tickets.appIdTicketsUrgencies({ params: { path: { appId } } }));
|
|
482
432
|
}
|
|
483
|
-
|
|
484
|
-
return
|
|
433
|
+
listTicketImpacts(appId) {
|
|
434
|
+
return this.sdk().then((s) => s.tickets.appIdTicketsImpacts({ params: { path: { appId } } }));
|
|
485
435
|
}
|
|
486
|
-
|
|
487
|
-
return
|
|
436
|
+
listTicketSources(appId) {
|
|
437
|
+
return this.sdk().then((s) => s.tickets.appIdTicketsSources({ params: { path: { appId } } }));
|
|
488
438
|
}
|
|
489
|
-
|
|
490
|
-
return
|
|
439
|
+
getTicketFeed(appId, ticketId) {
|
|
440
|
+
return this.sdk().then((s) => s.tickets.appIdTicketsIdFeed({ params: { path: { appId, id: ticketId } } }));
|
|
491
441
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
return
|
|
442
|
+
//
|
|
443
|
+
// Ticket Relationships
|
|
444
|
+
//
|
|
445
|
+
getTicketTasks(appId, ticketId) {
|
|
446
|
+
return this.sdk().then(
|
|
447
|
+
(s) => s.ticketRelationships.appIdTicketsTicketIdTasks({ params: { path: { appId, ticketId } } })
|
|
448
|
+
);
|
|
497
449
|
}
|
|
498
|
-
|
|
499
|
-
return
|
|
500
|
-
method: "POST",
|
|
501
|
-
body: JSON.stringify(body)
|
|
502
|
-
});
|
|
450
|
+
createTicketTask(appId, ticketId, body) {
|
|
451
|
+
return this.sdk().then((s) => s.ticketRelationships.createTicketTask({ appId, ticketId, body }));
|
|
503
452
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
});
|
|
509
|
-
return await this.requestJson(`/api/${appId}/tickets?${qs}`, {
|
|
510
|
-
method: "POST",
|
|
511
|
-
body: JSON.stringify(body)
|
|
512
|
-
});
|
|
453
|
+
listTicketAssets(appId, ticketId) {
|
|
454
|
+
return this.sdk().then(
|
|
455
|
+
(s) => s.ticketRelationships.appIdTicketsIdAssets({ params: { path: { appId, id: ticketId } } })
|
|
456
|
+
);
|
|
513
457
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
NotifyRequestor: String(notifyRequestor),
|
|
517
|
-
NotifyResponsible: String(notifyResponsible),
|
|
518
|
-
...comments ? { Comments: comments } : {},
|
|
519
|
-
IsPrivate: String(isPrivate)
|
|
520
|
-
});
|
|
521
|
-
return await this.requestJson(`/api/${appId}/tickets/${ticketId}?${qs}`, {
|
|
522
|
-
method: "PATCH",
|
|
523
|
-
body: JSON.stringify(body)
|
|
524
|
-
});
|
|
458
|
+
addTicketAsset(appId, ticketId, assetId) {
|
|
459
|
+
return this.sdk().then((s) => s.ticketRelationships.addTicketAsset({ appId, ticketId, assetId }));
|
|
525
460
|
}
|
|
526
|
-
|
|
527
|
-
return
|
|
528
|
-
|
|
529
|
-
body: JSON.stringify({
|
|
530
|
-
Body: body,
|
|
531
|
-
IsPrivate: isPrivate,
|
|
532
|
-
NotifyRequestor: notifyRequestor,
|
|
533
|
-
NotifyResponsible: notifyResponsible
|
|
534
|
-
})
|
|
461
|
+
removeTicketAsset(appId, ticketId, assetId) {
|
|
462
|
+
return this.sdk().then((s) => {
|
|
463
|
+
s.ticketRelationships.removeTicketAsset({ appId, ticketId, assetId, confirm: true });
|
|
535
464
|
});
|
|
536
465
|
}
|
|
537
|
-
|
|
538
|
-
return
|
|
466
|
+
getTicketContacts(appId, ticketId) {
|
|
467
|
+
return this.sdk().then(
|
|
468
|
+
(s) => s.ticketRelationships.appIdTicketsIdContacts({ params: { path: { appId, id: ticketId } } })
|
|
469
|
+
);
|
|
539
470
|
}
|
|
540
|
-
|
|
541
|
-
return
|
|
471
|
+
addTicketContact(appId, ticketId, contactUid) {
|
|
472
|
+
return this.sdk().then((s) => s.ticketRelationships.addTicketContact({ appId, ticketId, contactUid }));
|
|
542
473
|
}
|
|
543
|
-
|
|
544
|
-
return
|
|
545
|
-
|
|
546
|
-
body: JSON.stringify(body)
|
|
474
|
+
removeTicketContact(appId, ticketId, contactUid) {
|
|
475
|
+
return this.sdk().then((s) => {
|
|
476
|
+
s.ticketRelationships.removeTicketContact({ appId, ticketId, contactUid, confirm: true });
|
|
547
477
|
});
|
|
548
478
|
}
|
|
549
|
-
|
|
550
|
-
|
|
479
|
+
//
|
|
480
|
+
// People
|
|
481
|
+
//
|
|
482
|
+
getUser(uid) {
|
|
483
|
+
return this.sdk().then((s) => s.people.peopleUid({ params: { path: { uid } } }));
|
|
551
484
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
body: JSON.stringify({})
|
|
556
|
-
});
|
|
485
|
+
searchUsers(body) {
|
|
486
|
+
const searchText = typeof body["SearchText"] === "string" ? body["SearchText"] : "";
|
|
487
|
+
return this.sdk().then((s) => s.people.peopleLookup({ body: { SearchText: searchText } }));
|
|
557
488
|
}
|
|
558
|
-
|
|
559
|
-
|
|
489
|
+
getGroup(groupId) {
|
|
490
|
+
return this.sdk().then((s) => s.people.groupsId({ params: { path: { id: groupId } } }));
|
|
560
491
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
// -------------------------------------------------------------------------
|
|
564
|
-
async getUser(uid) {
|
|
565
|
-
return await this.requestJson(`/api/people/${encodeURIComponent(uid)}`);
|
|
492
|
+
searchGroups(_body) {
|
|
493
|
+
return this.sdk().then((s) => s.people.searchGroups({ body: {} }));
|
|
566
494
|
}
|
|
567
|
-
|
|
568
|
-
return
|
|
569
|
-
method: "POST",
|
|
570
|
-
body: JSON.stringify(body)
|
|
571
|
-
});
|
|
495
|
+
getGroupMembers(groupId) {
|
|
496
|
+
return this.sdk().then((s) => s.people.groupsIdMembers({ params: { path: { id: groupId } } }));
|
|
572
497
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
return
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
498
|
+
//
|
|
499
|
+
// Knowledge Base
|
|
500
|
+
//
|
|
501
|
+
getKbArticle(appId, articleId) {
|
|
502
|
+
return this.sdk().then((s) => s.knowledgeBase.appIdKnowledgebaseId({ params: { path: { appId, id: articleId } } }));
|
|
503
|
+
}
|
|
504
|
+
searchKbArticles(appId, body) {
|
|
505
|
+
const keyword = typeof body["SearchText"] === "string" ? body["SearchText"] : "";
|
|
506
|
+
return this.sdk().then((s) => s.knowledgeBase.appIdKnowledgebaseId({ params: { path: { appId, id: 0 } } })).then(
|
|
507
|
+
(r) => Array.isArray(r) ? r.filter((item) => {
|
|
508
|
+
const s = typeof item === "object" && item !== null ? JSON.stringify(item).toLowerCase() : "";
|
|
509
|
+
return keyword ? s.includes(keyword.toLowerCase()) : true;
|
|
510
|
+
}) : []
|
|
511
|
+
);
|
|
581
512
|
}
|
|
582
|
-
|
|
583
|
-
return
|
|
513
|
+
listKbCategories(appId) {
|
|
514
|
+
return this.sdk().then(
|
|
515
|
+
(s) => s.knowledgeBase.appIdKnowledgebaseCategories({ params: { path: { appId } } })
|
|
516
|
+
);
|
|
584
517
|
}
|
|
585
|
-
//
|
|
586
|
-
//
|
|
587
|
-
//
|
|
588
|
-
|
|
589
|
-
return
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
return
|
|
593
|
-
|
|
594
|
-
|
|
518
|
+
//
|
|
519
|
+
// Assets
|
|
520
|
+
//
|
|
521
|
+
getAsset(appId, assetId) {
|
|
522
|
+
return this.sdk().then((s) => s.assets.appIdAssetsId({ params: { path: { appId, id: assetId } } }));
|
|
523
|
+
}
|
|
524
|
+
searchAssets(appId, _body) {
|
|
525
|
+
return this.sdk().then((s) => s.assets.appIdAssetsFeed({ params: { path: { appId } } }));
|
|
526
|
+
}
|
|
527
|
+
listAssetStatuses(appId) {
|
|
528
|
+
return this.sdk().then((s) => s.assets.appIdAssetsStatuses({ params: { path: { appId } } }));
|
|
529
|
+
}
|
|
530
|
+
listProductModels(appId) {
|
|
531
|
+
return this.sdk().then((s) => s.assets.appIdAssetsModels({ params: { path: { appId } } }));
|
|
532
|
+
}
|
|
533
|
+
listVendors(appId) {
|
|
534
|
+
return this.sdk().then((s) => s.cmdb.appIdAssetsVendors({ params: { path: { appId } } }));
|
|
535
|
+
}
|
|
536
|
+
//
|
|
537
|
+
// CMDB
|
|
538
|
+
//
|
|
539
|
+
getConfigurationItem(appId, ciId) {
|
|
540
|
+
return this.sdk().then((s) => s.cmdb.appIdCmdbId({ params: { path: { appId, id: ciId } } }));
|
|
541
|
+
}
|
|
542
|
+
searchConfigurationItems(appId, _body) {
|
|
543
|
+
return this.sdk().then((s) => s.cmdb.appIdCmdbSearches({ params: { path: { appId } } }));
|
|
544
|
+
}
|
|
545
|
+
listCiTypes(appId) {
|
|
546
|
+
return this.sdk().then((s) => s.cmdb.appIdCmdbTypes({ params: { path: { appId } } }));
|
|
547
|
+
}
|
|
548
|
+
listCiRelationshipTypes() {
|
|
549
|
+
return this.sdk().then((s) => s.cmdb.appIdCmdbRelationshiptypes());
|
|
550
|
+
}
|
|
551
|
+
//
|
|
552
|
+
// Services
|
|
553
|
+
//
|
|
554
|
+
listServiceCatalog(appId) {
|
|
555
|
+
return this.sdk().then((s) => s.services.appIdServices({ params: { path: { appId } } }));
|
|
556
|
+
}
|
|
557
|
+
getService(appId, serviceId) {
|
|
558
|
+
return this.sdk().then((s) => s.services.appIdServicesId({ params: { path: { appId, id: serviceId } } }));
|
|
559
|
+
}
|
|
560
|
+
searchServices(appId, body) {
|
|
561
|
+
const keyword = typeof body["Keyword"] === "string" ? body["Keyword"] : "";
|
|
562
|
+
return this.sdk().then((s) => s.services.appIdServices({ params: { path: { appId } } })).then((r) => {
|
|
563
|
+
if (!Array.isArray(r)) return [];
|
|
564
|
+
if (!keyword) return r;
|
|
565
|
+
const lower = keyword.toLowerCase();
|
|
566
|
+
return r.filter((item) => {
|
|
567
|
+
const text = typeof item === "object" && item !== null ? JSON.stringify(item).toLowerCase() : "";
|
|
568
|
+
return text.includes(lower);
|
|
569
|
+
});
|
|
595
570
|
});
|
|
596
571
|
}
|
|
597
|
-
|
|
598
|
-
return
|
|
572
|
+
listServiceCategories(appId) {
|
|
573
|
+
return this.sdk().then((s) => s.services.appIdServicesCategories({ params: { path: { appId } } }));
|
|
599
574
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
});
|
|
575
|
+
//
|
|
576
|
+
// Projects
|
|
577
|
+
//
|
|
578
|
+
getProject(projectId) {
|
|
579
|
+
return this.sdk().then((s) => s.projects.projectsId({ params: { path: { id: projectId } } }));
|
|
605
580
|
}
|
|
606
|
-
|
|
607
|
-
return
|
|
608
|
-
method: "PUT",
|
|
609
|
-
body: JSON.stringify(body)
|
|
610
|
-
});
|
|
581
|
+
searchProjects(_body) {
|
|
582
|
+
return this.sdk().then((s) => s.projects.projectsFeed());
|
|
611
583
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
// -------------------------------------------------------------------------
|
|
615
|
-
async getAsset(appId, assetId) {
|
|
616
|
-
return await this.requestJson(`/api/${appId}/assets/${assetId}`);
|
|
584
|
+
listProjectTypes() {
|
|
585
|
+
return this.sdk().then((s) => s.projects.projectsTypes());
|
|
617
586
|
}
|
|
618
|
-
|
|
619
|
-
return
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
});
|
|
587
|
+
getProjectPlans(projectId) {
|
|
588
|
+
return this.sdk().then(
|
|
589
|
+
(s) => s.projects.projectsProjectIDPlansPlanID({ params: { path: { id: projectId } } })
|
|
590
|
+
);
|
|
623
591
|
}
|
|
624
|
-
|
|
625
|
-
return
|
|
592
|
+
getProjectIssues(projectId) {
|
|
593
|
+
return this.sdk().then(
|
|
594
|
+
(s) => s.projects.projectsProjectIdIssuesCategories({ params: { path: { id: projectId } } })
|
|
595
|
+
);
|
|
626
596
|
}
|
|
627
|
-
|
|
628
|
-
return
|
|
597
|
+
getProjectRisks(projectId) {
|
|
598
|
+
return this.sdk().then(
|
|
599
|
+
(s) => s.projects.projectsProjectIdRisksCategories({ params: { path: { id: projectId } } })
|
|
600
|
+
);
|
|
629
601
|
}
|
|
630
|
-
|
|
631
|
-
|
|
602
|
+
//
|
|
603
|
+
// Time
|
|
604
|
+
//
|
|
605
|
+
listTimeTypes() {
|
|
606
|
+
return this.sdk().then((s) => s.time.timeTypes());
|
|
632
607
|
}
|
|
633
|
-
|
|
634
|
-
return
|
|
608
|
+
getMyTimeEntries(_startDate, _endDate) {
|
|
609
|
+
return this.sdk().then((s) => s.time.timeId({ params: { path: { id: 0 } } }));
|
|
635
610
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
611
|
+
//
|
|
612
|
+
// Reference Data
|
|
613
|
+
//
|
|
614
|
+
listAccounts(_appId) {
|
|
615
|
+
return this.sdk().then((s) => s.referenceData.accounts());
|
|
641
616
|
}
|
|
642
|
-
|
|
643
|
-
return
|
|
617
|
+
getAccount(accountId) {
|
|
618
|
+
return this.sdk().then((s) => s.referenceData.accountsId({ params: { path: { id: accountId } } }));
|
|
644
619
|
}
|
|
645
|
-
|
|
646
|
-
return
|
|
620
|
+
listLocations() {
|
|
621
|
+
return this.sdk().then((s) => s.referenceData.locations());
|
|
647
622
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
// -------------------------------------------------------------------------
|
|
651
|
-
async listServiceCatalog(appId) {
|
|
652
|
-
return await this.requestJson(`/api/${appId}/services`);
|
|
623
|
+
listFunctionalRoles() {
|
|
624
|
+
return this.sdk().then((s) => s.referenceData.securityrolesPermissions());
|
|
653
625
|
}
|
|
654
|
-
|
|
655
|
-
return
|
|
626
|
+
listCustomAttributes(_componentId, _appId, _associatedTypeId) {
|
|
627
|
+
return this.sdk().then((s) => s.referenceData.attributesCustom());
|
|
656
628
|
}
|
|
657
|
-
|
|
658
|
-
return
|
|
659
|
-
method: "POST",
|
|
660
|
-
body: JSON.stringify(body)
|
|
661
|
-
});
|
|
629
|
+
listTicketStatuses(appId) {
|
|
630
|
+
return this.sdk().then((s) => s.referenceData.appIdTicketsStatuses({ params: { path: { appId } } }));
|
|
662
631
|
}
|
|
663
|
-
//
|
|
664
|
-
//
|
|
665
|
-
//
|
|
666
|
-
|
|
667
|
-
return
|
|
668
|
-
}
|
|
669
|
-
async searchProjects(body) {
|
|
670
|
-
return await this.requestJson("/api/projects/search", {
|
|
671
|
-
method: "POST",
|
|
672
|
-
body: JSON.stringify(body)
|
|
673
|
-
});
|
|
632
|
+
//
|
|
633
|
+
// Mutations
|
|
634
|
+
//
|
|
635
|
+
createTicket(appId, body, _notifyRequestor, _notifyResponsible) {
|
|
636
|
+
return this.sdk().then((s) => s.tickets.createTicket({ appId, body }));
|
|
674
637
|
}
|
|
675
|
-
|
|
676
|
-
return
|
|
638
|
+
updateTicket(appId, ticketId, body, _notifyRequestor, _notifyResponsible, _comments, _isPrivate) {
|
|
639
|
+
return this.sdk().then((s) => s.tickets.updateTicket({ appId, ticketId, body }));
|
|
677
640
|
}
|
|
678
|
-
|
|
679
|
-
return
|
|
641
|
+
addTicketComment(appId, ticketId, body, _isPrivate, _notifyRequestor, _notifyResponsible) {
|
|
642
|
+
return this.sdk().then((s) => s.tickets.addTicketComment({ appId, ticketId, body: { Body: body } }));
|
|
680
643
|
}
|
|
681
|
-
|
|
682
|
-
return
|
|
644
|
+
createKbArticle(appId, body) {
|
|
645
|
+
return this.sdk().then((s) => s.knowledgeBase.createArticle({ appId, body }));
|
|
683
646
|
}
|
|
684
|
-
|
|
685
|
-
return
|
|
647
|
+
updateKbArticle(appId, articleId, body) {
|
|
648
|
+
return this.sdk().then((s) => s.knowledgeBase.updateArticle({ appId, articleId, body }));
|
|
686
649
|
}
|
|
687
|
-
|
|
688
|
-
return
|
|
689
|
-
method: "POST",
|
|
690
|
-
body: JSON.stringify(body)
|
|
691
|
-
});
|
|
650
|
+
createProjectIssue(_projectId, body) {
|
|
651
|
+
return this.sdk().then((s) => s.projects.createIssue({ body }));
|
|
692
652
|
}
|
|
693
|
-
|
|
694
|
-
return
|
|
695
|
-
method: "POST",
|
|
696
|
-
body: JSON.stringify(body)
|
|
697
|
-
});
|
|
653
|
+
createProjectRisk(_projectId, body) {
|
|
654
|
+
return this.sdk().then((s) => s.projects.createRisk({ body }));
|
|
698
655
|
}
|
|
699
|
-
|
|
700
|
-
|
|
656
|
+
//
|
|
657
|
+
// Delete mutations (confirm:true required for all)
|
|
658
|
+
//
|
|
659
|
+
deleteAsset(appId, assetId) {
|
|
660
|
+
return this.sdk().then((s) => s.assets.deleteAsset({ appId, assetId, confirm: true }));
|
|
701
661
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
return await this.requestJson(`/api/time/entries?${qs}`);
|
|
662
|
+
deleteConfigurationItem(appId, ciId) {
|
|
663
|
+
return this.sdk().then((s) => s.cmdb.deleteConfigurationItem({ appId, configurationItemId: ciId, confirm: true }));
|
|
705
664
|
}
|
|
706
|
-
|
|
707
|
-
return
|
|
665
|
+
deleteService(appId, serviceId) {
|
|
666
|
+
return this.sdk().then((s) => s.services.deleteService({ appId, serviceId, confirm: true }));
|
|
708
667
|
}
|
|
709
|
-
|
|
710
|
-
return
|
|
711
|
-
method: "PUT"
|
|
712
|
-
});
|
|
668
|
+
deleteServiceCategory(appId, categoryId) {
|
|
669
|
+
return this.sdk().then((s) => s.services.deleteServiceCategory({ appId, categoryId, confirm: true }));
|
|
713
670
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
method: "DELETE"
|
|
717
|
-
});
|
|
671
|
+
deleteTimeEntry(timeEntryId) {
|
|
672
|
+
return this.sdk().then((s) => s.time.deleteTimeEntry({ timeEntryId, confirm: true }));
|
|
718
673
|
}
|
|
719
|
-
|
|
720
|
-
|
|
674
|
+
};
|
|
675
|
+
function createConfiguredTeamDynamixClient() {
|
|
676
|
+
return new UnifiedTeamDynamixClient(getTeamDynamixConfig());
|
|
677
|
+
}
|
|
678
|
+
function assertWriteToolsEnabled(config) {
|
|
679
|
+
if (!config.enableWriteTools) {
|
|
680
|
+
throw new Error("Write tools are disabled. Set TEAMDYNAMIX_ENABLE_WRITE_TOOLS=true in your environment.");
|
|
721
681
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
if (response.status === 429 && attempt < cappedRetries) {
|
|
748
|
-
const rateLimit = parseRateLimit(response.headers);
|
|
749
|
-
if (LOG_LEVEL === "debug") {
|
|
750
|
-
console.error(
|
|
751
|
-
`[teamdynamix-mcp] rate limited on ${normalizePath(path)}; retry ${attempt + 1}/${cappedRetries} in ${rateLimit.waitMs}ms`
|
|
752
|
-
);
|
|
753
|
-
}
|
|
754
|
-
await sleep(rateLimit.waitMs);
|
|
755
|
-
continue;
|
|
756
|
-
}
|
|
757
|
-
if (!response.ok) {
|
|
758
|
-
await response.text();
|
|
759
|
-
throw new Error(`TeamDynamix request failed (${response.status}) for ${normalizePath(path)}.`);
|
|
760
|
-
}
|
|
761
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
762
|
-
if (contentType.includes("application/json")) {
|
|
763
|
-
const data = await response.json();
|
|
764
|
-
if (Array.isArray(data)) {
|
|
765
|
-
TeamDynamixListResponseSchema.parse(data);
|
|
766
|
-
} else if (typeof data === "object" && data !== null) {
|
|
767
|
-
TeamDynamixSingleResponseSchema.parse(data);
|
|
768
|
-
}
|
|
769
|
-
return data;
|
|
770
|
-
}
|
|
771
|
-
const text = await response.text();
|
|
772
|
-
return text;
|
|
773
|
-
}
|
|
774
|
-
throw new Error(`TeamDynamix request exceeded retry budget for ${normalizePath(path)}.`);
|
|
775
|
-
}
|
|
776
|
-
async getBearerToken(requireAdmin) {
|
|
777
|
-
const configStatus = getTeamDynamixConfigStatus(this.config);
|
|
778
|
-
if (!configStatus.configured) {
|
|
779
|
-
throw new Error(
|
|
780
|
-
`TeamDynamix is not configured. Missing: ${configStatus.missing.join(", ")}. Populate the required environment variables in .env before using TeamDynamix tools.`
|
|
781
|
-
);
|
|
782
|
-
}
|
|
783
|
-
if (requireAdmin && this.config.authMode !== "admin") {
|
|
784
|
-
throw new Error(
|
|
785
|
-
"This TeamDynamix action requires admin authentication, but TEAMDYNAMIX_AUTH_MODE is not set to admin."
|
|
786
|
-
);
|
|
682
|
+
}
|
|
683
|
+
function assertDeleteToolsEnabled(config) {
|
|
684
|
+
if (!config.enableDeleteTools) {
|
|
685
|
+
throw new Error("Delete tools are disabled. Set TEAMDYNAMIX_ENABLE_DELETE_TOOLS=true in your environment.");
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// src/services/teamdynamix/render.service.ts
|
|
690
|
+
import { tablemark } from "tablemark";
|
|
691
|
+
function render(payload, responseFormat, characterLimit = CHARACTER_LIMIT) {
|
|
692
|
+
if (responseFormat === "json") {
|
|
693
|
+
if (typeof payload === "undefined") return "undefined";
|
|
694
|
+
return JSON.stringify(payload, null, 2);
|
|
695
|
+
}
|
|
696
|
+
const markdown = renderMarkdown(payload);
|
|
697
|
+
return truncateMarkdown(markdown, characterLimit);
|
|
698
|
+
}
|
|
699
|
+
function renderMarkdown(payload) {
|
|
700
|
+
if (payload == null) return "";
|
|
701
|
+
if (typeof payload !== "object") {
|
|
702
|
+
return String(payload);
|
|
703
|
+
}
|
|
704
|
+
if (Array.isArray(payload)) {
|
|
705
|
+
if (payload.length === 0) {
|
|
706
|
+
return "";
|
|
787
707
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
return this.cachedToken.token;
|
|
708
|
+
if (payload.every(isPlainObject)) {
|
|
709
|
+
return renderArrayAsMarkdownTable(payload);
|
|
791
710
|
}
|
|
792
|
-
|
|
793
|
-
return token;
|
|
711
|
+
return "```json\n" + JSON.stringify(payload, null, 2) + "\n```";
|
|
794
712
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
713
|
+
if (isPlainObject(payload)) {
|
|
714
|
+
const hasArrayOfObjects = Object.values(payload).some(isArrayOfPlainObjects);
|
|
715
|
+
if (hasArrayOfObjects) {
|
|
716
|
+
return renderObjectWithTables(payload);
|
|
798
717
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
718
|
+
return renderObjectAsMarkdownList(payload);
|
|
719
|
+
}
|
|
720
|
+
return "";
|
|
721
|
+
}
|
|
722
|
+
function isPlainObject(value) {
|
|
723
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
724
|
+
}
|
|
725
|
+
function isArrayOfPlainObjects(value) {
|
|
726
|
+
return Array.isArray(value) && value.length > 0 && value.every(isPlainObject);
|
|
727
|
+
}
|
|
728
|
+
function serializeValue(value) {
|
|
729
|
+
if (typeof value !== "object" || value === null) return String(value);
|
|
730
|
+
if (Array.isArray(value)) return value.map(serializeValue).join(", ");
|
|
731
|
+
return "{ " + Object.entries(value).map(([k, v]) => `${k}: ${typeof v === "object" && v !== null ? "[Object]" : String(v)}`).join(", ") + " }";
|
|
732
|
+
}
|
|
733
|
+
function renderObjectAsMarkdownList(obj) {
|
|
734
|
+
const lines = [];
|
|
735
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
736
|
+
lines.push(`- **${key}**: ${truncateCell(serializeValue(val))}`);
|
|
737
|
+
}
|
|
738
|
+
return lines.join("\n");
|
|
739
|
+
}
|
|
740
|
+
function renderObjectWithTables(obj) {
|
|
741
|
+
const lines = [];
|
|
742
|
+
const scalarEntries = [];
|
|
743
|
+
const arrayEntries = [];
|
|
744
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
745
|
+
if (isArrayOfPlainObjects(val)) {
|
|
746
|
+
arrayEntries.push([key, val]);
|
|
747
|
+
} else {
|
|
748
|
+
scalarEntries.push([key, val]);
|
|
817
749
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
750
|
+
}
|
|
751
|
+
if (scalarEntries.length > 0) {
|
|
752
|
+
lines.push("**Metadata:**");
|
|
753
|
+
for (const [key, val] of scalarEntries) {
|
|
754
|
+
lines.push(`- **${key}**: ${truncateCell(serializeValue(val))}`);
|
|
821
755
|
}
|
|
822
|
-
|
|
823
|
-
const payload = contentType.includes("application/json") ? await response.json() : await response.text();
|
|
824
|
-
const token = extractAuthToken(payload);
|
|
825
|
-
const expiryEpochSeconds = decodeJwtExpiryEpochSeconds(token);
|
|
826
|
-
this.cachedToken = {
|
|
827
|
-
token,
|
|
828
|
-
expiresAtMs: expiryEpochSeconds ? expiryEpochSeconds * 1e3 : null
|
|
829
|
-
};
|
|
830
|
-
return token;
|
|
756
|
+
lines.push("");
|
|
831
757
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
758
|
+
for (const [key, arr] of arrayEntries) {
|
|
759
|
+
lines.push(`**${key}:**`);
|
|
760
|
+
const table = renderArrayAsMarkdownTable(arr);
|
|
761
|
+
lines.push(table);
|
|
762
|
+
lines.push("");
|
|
763
|
+
}
|
|
764
|
+
return lines.join("\n");
|
|
765
|
+
}
|
|
766
|
+
function renderArrayAsMarkdownTable(arr) {
|
|
767
|
+
const toCellText = ({ value }) => serializeValue(value);
|
|
768
|
+
let table = tablemark(arr, {
|
|
769
|
+
toCellText,
|
|
770
|
+
headerCase: "preserve"
|
|
771
|
+
});
|
|
772
|
+
table = table.replace(/\| +/g, "| ").replace(/ +\|/g, " |");
|
|
773
|
+
table = table.replace(
|
|
774
|
+
/^(\| +:)(-+)( +\|)/gm,
|
|
775
|
+
(_match, _pre, _dashes, _post, offset, str) => {
|
|
776
|
+
if (str.slice(0, offset).includes(":---")) return _match;
|
|
777
|
+
const headerLine = str.slice(0, offset).split("\n").reverse().find((l) => l.startsWith("|"));
|
|
778
|
+
if (!headerLine) return _match;
|
|
779
|
+
const colCount = headerLine.split("|").length - 2;
|
|
780
|
+
const dashCounts = Array.from({ length: colCount }, (_, i) => 3 + i);
|
|
781
|
+
const newRow = [""].concat(dashCounts.map((n) => ` :${"-".repeat(n)} `)).concat([""]).join("|");
|
|
782
|
+
return newRow;
|
|
783
|
+
}
|
|
784
|
+
);
|
|
785
|
+
return table;
|
|
786
|
+
}
|
|
787
|
+
function truncateCell(str) {
|
|
788
|
+
const MAX = 120;
|
|
789
|
+
if (str.length <= MAX) return str;
|
|
790
|
+
return str.slice(0, MAX) + "\u2026";
|
|
791
|
+
}
|
|
792
|
+
function truncateMarkdown(markdown, limit) {
|
|
793
|
+
if (markdown.length <= limit) return markdown;
|
|
794
|
+
const ellipsis = "...";
|
|
795
|
+
if (limit <= ellipsis.length) return markdown.slice(0, limit);
|
|
796
|
+
const truncated = markdown.slice(0, limit - ellipsis.length).replace(/\n+$/, "");
|
|
797
|
+
return truncated + ellipsis;
|
|
835
798
|
}
|
|
836
799
|
|
|
837
800
|
// src/tools/teamdynamix.domain-gateways.tools.ts
|
|
@@ -882,20 +845,24 @@ var teamdynamixAssetsActionSchema = z4.enum([
|
|
|
882
845
|
"get_asset",
|
|
883
846
|
"search_assets",
|
|
884
847
|
"list_asset_statuses",
|
|
885
|
-
"list_product_models"
|
|
848
|
+
"list_product_models",
|
|
849
|
+
"delete_asset"
|
|
886
850
|
]);
|
|
887
851
|
var teamdynamixCmdbActionSchema = z4.enum([
|
|
888
852
|
"get_ci",
|
|
889
853
|
"search_cis",
|
|
890
854
|
"list_ci_types",
|
|
891
855
|
"list_ci_relationship_types",
|
|
892
|
-
"list_vendors"
|
|
856
|
+
"list_vendors",
|
|
857
|
+
"delete_ci"
|
|
893
858
|
]);
|
|
894
859
|
var teamdynamixServicesActionSchema = z4.enum([
|
|
895
860
|
"list_services",
|
|
896
861
|
"get_service",
|
|
897
862
|
"search_services",
|
|
898
|
-
"list_service_categories"
|
|
863
|
+
"list_service_categories",
|
|
864
|
+
"delete_service",
|
|
865
|
+
"delete_service_category"
|
|
899
866
|
]);
|
|
900
867
|
var teamdynamixProjectsActionSchema = z4.enum([
|
|
901
868
|
"get_project",
|
|
@@ -916,11 +883,6 @@ var teamdynamixReferenceDataActionSchema = z4.enum([
|
|
|
916
883
|
"list_custom_attributes"
|
|
917
884
|
]);
|
|
918
885
|
var gatewayPayloadSchema = z4.record(z4.string(), z4.unknown()).default({});
|
|
919
|
-
function render(data, responseFormat) {
|
|
920
|
-
if (responseFormat === "json") return JSON.stringify(data, null, 2);
|
|
921
|
-
if (typeof data === "string") return data;
|
|
922
|
-
return JSON.stringify(data, null, 2);
|
|
923
|
-
}
|
|
924
886
|
function messageFromError(error) {
|
|
925
887
|
return error instanceof Error ? error.message : String(error);
|
|
926
888
|
}
|
|
@@ -1527,6 +1489,23 @@ function registerTeamDynamixAssetsGateway(server2) {
|
|
|
1527
1489
|
const models = await client.listProductModels(parsed.app_id);
|
|
1528
1490
|
return toSuccessResponse({ appId: parsed.app_id, count: models.length, models }, response_format);
|
|
1529
1491
|
}
|
|
1492
|
+
case "delete_asset": {
|
|
1493
|
+
const parsed = parsePayload(
|
|
1494
|
+
z4.object({
|
|
1495
|
+
app_id: TeamDynamixAppIdSchema,
|
|
1496
|
+
asset_id: z4.number().int().positive(),
|
|
1497
|
+
confirm: z4.literal(true)
|
|
1498
|
+
}),
|
|
1499
|
+
payload,
|
|
1500
|
+
action
|
|
1501
|
+
);
|
|
1502
|
+
assertDeleteToolsEnabled(getTeamDynamixConfig());
|
|
1503
|
+
await client.deleteAsset(parsed.app_id, parsed.asset_id);
|
|
1504
|
+
return toSuccessResponse(
|
|
1505
|
+
{ appId: parsed.app_id, assetId: parsed.asset_id, deleted: true },
|
|
1506
|
+
response_format
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1530
1509
|
}
|
|
1531
1510
|
} catch (error) {
|
|
1532
1511
|
return toErrorResponse(error);
|
|
@@ -1586,6 +1565,20 @@ function registerTeamDynamixCmdbGateway(server2) {
|
|
|
1586
1565
|
const vendors = await client.listVendors(parsed.app_id);
|
|
1587
1566
|
return toSuccessResponse({ appId: parsed.app_id, count: vendors.length, vendors }, response_format);
|
|
1588
1567
|
}
|
|
1568
|
+
case "delete_ci": {
|
|
1569
|
+
const parsed = parsePayload(
|
|
1570
|
+
z4.object({
|
|
1571
|
+
app_id: TeamDynamixAppIdSchema,
|
|
1572
|
+
ci_id: z4.number().int().positive(),
|
|
1573
|
+
confirm: z4.literal(true)
|
|
1574
|
+
}),
|
|
1575
|
+
payload,
|
|
1576
|
+
action
|
|
1577
|
+
);
|
|
1578
|
+
assertDeleteToolsEnabled(getTeamDynamixConfig());
|
|
1579
|
+
await client.deleteConfigurationItem(parsed.app_id, parsed.ci_id);
|
|
1580
|
+
return toSuccessResponse({ appId: parsed.app_id, ciId: parsed.ci_id, deleted: true }, response_format);
|
|
1581
|
+
}
|
|
1589
1582
|
}
|
|
1590
1583
|
} catch (error) {
|
|
1591
1584
|
return toErrorResponse(error);
|
|
@@ -1638,6 +1631,40 @@ function registerTeamDynamixServicesGateway(server2) {
|
|
|
1638
1631
|
const categories = await client.listServiceCategories(parsed.app_id);
|
|
1639
1632
|
return toSuccessResponse({ appId: parsed.app_id, count: categories.length, categories }, response_format);
|
|
1640
1633
|
}
|
|
1634
|
+
case "delete_service": {
|
|
1635
|
+
const parsed = parsePayload(
|
|
1636
|
+
z4.object({
|
|
1637
|
+
app_id: TeamDynamixAppIdSchema,
|
|
1638
|
+
service_id: z4.number().int().positive(),
|
|
1639
|
+
confirm: z4.literal(true)
|
|
1640
|
+
}),
|
|
1641
|
+
payload,
|
|
1642
|
+
action
|
|
1643
|
+
);
|
|
1644
|
+
assertDeleteToolsEnabled(getTeamDynamixConfig());
|
|
1645
|
+
await client.deleteService(parsed.app_id, parsed.service_id);
|
|
1646
|
+
return toSuccessResponse(
|
|
1647
|
+
{ appId: parsed.app_id, serviceId: parsed.service_id, deleted: true },
|
|
1648
|
+
response_format
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
case "delete_service_category": {
|
|
1652
|
+
const parsed = parsePayload(
|
|
1653
|
+
z4.object({
|
|
1654
|
+
app_id: TeamDynamixAppIdSchema,
|
|
1655
|
+
category_id: z4.number().int().positive(),
|
|
1656
|
+
confirm: z4.literal(true)
|
|
1657
|
+
}),
|
|
1658
|
+
payload,
|
|
1659
|
+
action
|
|
1660
|
+
);
|
|
1661
|
+
assertDeleteToolsEnabled(getTeamDynamixConfig());
|
|
1662
|
+
await client.deleteServiceCategory(parsed.app_id, parsed.category_id);
|
|
1663
|
+
return toSuccessResponse(
|
|
1664
|
+
{ appId: parsed.app_id, categoryId: parsed.category_id, deleted: true },
|
|
1665
|
+
response_format
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1641
1668
|
}
|
|
1642
1669
|
} catch (error) {
|
|
1643
1670
|
return toErrorResponse(error);
|