@codazen/harmonica-mcp 5.0.0 → 5.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/dist/index.js +228 -126
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22032,22 +22032,33 @@ function registerAuditIntegrityTools(server, ctx, client) {
|
|
|
22032
22032
|
);
|
|
22033
22033
|
}
|
|
22034
22034
|
|
|
22035
|
-
// ../../libs/harmonica-services/src/
|
|
22035
|
+
// ../../libs/harmonica-services/src/tool-schemas/bar.ts
|
|
22036
22036
|
var BAR_STATUSES = ["open", "closed"];
|
|
22037
22037
|
var idSegmentSchema = external_exports.string().min(1).max(128).refine((id) => !id.includes("#"), {
|
|
22038
22038
|
message: "must not contain '#' \u2014 it is a DynamoDB key delimiter"
|
|
22039
22039
|
});
|
|
22040
|
+
var create_bar = {
|
|
22041
|
+
accountId: idSegmentSchema.describe("The Account ID this Bar belongs to"),
|
|
22042
|
+
barId: idSegmentSchema.describe(
|
|
22043
|
+
'Caller-supplied Bar ID (must not contain "#")'
|
|
22044
|
+
),
|
|
22045
|
+
startDate: external_exports.string().datetime().describe("ISO 8601 start of the planning period"),
|
|
22046
|
+
endDate: external_exports.string().datetime().describe("ISO 8601 end of the planning period; must be after startDate"),
|
|
22047
|
+
status: external_exports.enum(BAR_STATUSES).optional().describe('Defaults to "open"')
|
|
22048
|
+
};
|
|
22049
|
+
var get_bar = {
|
|
22050
|
+
barId: idSegmentSchema.describe("The Bar ID")
|
|
22051
|
+
};
|
|
22052
|
+
var list_account_bars = {
|
|
22053
|
+
accountId: idSegmentSchema.describe("The Account ID")
|
|
22054
|
+
};
|
|
22055
|
+
|
|
22056
|
+
// ../../libs/harmonica-services/src/mcp/tools/bar-tools.ts
|
|
22040
22057
|
function registerBarTools(server, _ctx, client) {
|
|
22041
22058
|
server.tool(
|
|
22042
22059
|
"create_bar",
|
|
22043
22060
|
"Create a Bar \u2014 the recurring planning-period time-box a client Account plans and delivers within (e.g. one or two weeks). Bars are Account-scoped and independent of any CadenceConfig already set on the Account; the caller supplies explicit start/end dates.",
|
|
22044
|
-
|
|
22045
|
-
accountId: idSegmentSchema.describe("The Account ID this Bar belongs to"),
|
|
22046
|
-
barId: idSegmentSchema.describe('Caller-supplied Bar ID (must not contain "#")'),
|
|
22047
|
-
startDate: external_exports.string().datetime().describe("ISO 8601 start of the planning period"),
|
|
22048
|
-
endDate: external_exports.string().datetime().describe("ISO 8601 end of the planning period; must be after startDate"),
|
|
22049
|
-
status: external_exports.enum(BAR_STATUSES).optional().describe('Defaults to "open"')
|
|
22050
|
-
},
|
|
22061
|
+
create_bar,
|
|
22051
22062
|
async ({ accountId, barId, startDate, endDate, status }) => {
|
|
22052
22063
|
const startMs = new Date(startDate).getTime();
|
|
22053
22064
|
const endMs = new Date(endDate).getTime();
|
|
@@ -22077,9 +22088,7 @@ function registerBarTools(server, _ctx, client) {
|
|
|
22077
22088
|
server.tool(
|
|
22078
22089
|
"get_bar",
|
|
22079
22090
|
"Get a Bar by ID.",
|
|
22080
|
-
|
|
22081
|
-
barId: idSegmentSchema.describe("The Bar ID")
|
|
22082
|
-
},
|
|
22091
|
+
get_bar,
|
|
22083
22092
|
async ({ barId }) => {
|
|
22084
22093
|
try {
|
|
22085
22094
|
const bar = await client.getBar(barId);
|
|
@@ -22098,28 +22107,28 @@ function registerBarTools(server, _ctx, client) {
|
|
|
22098
22107
|
}
|
|
22099
22108
|
}
|
|
22100
22109
|
);
|
|
22101
|
-
|
|
22102
|
-
|
|
22103
|
-
|
|
22104
|
-
|
|
22105
|
-
|
|
22106
|
-
|
|
22107
|
-
|
|
22108
|
-
|
|
22109
|
-
|
|
22110
|
-
|
|
22111
|
-
|
|
22112
|
-
|
|
22113
|
-
|
|
22114
|
-
|
|
22110
|
+
for (const name of ["list_account_bars", "list_bars"]) {
|
|
22111
|
+
server.tool(
|
|
22112
|
+
name,
|
|
22113
|
+
"List an Account's Bars, chronological by start date.",
|
|
22114
|
+
list_account_bars,
|
|
22115
|
+
async ({ accountId }) => {
|
|
22116
|
+
try {
|
|
22117
|
+
const bars = await client.listAccountBars(accountId);
|
|
22118
|
+
if (bars === void 0) {
|
|
22119
|
+
return { content: [{ type: "text", text: `Account not found: ${accountId}` }], isError: true };
|
|
22120
|
+
}
|
|
22121
|
+
if (bars.length === 0) {
|
|
22122
|
+
return { content: [{ type: "text", text: "No bars found." }] };
|
|
22123
|
+
}
|
|
22124
|
+
const text = bars.map((b) => `${b.barId} ${b.startDate} \u2192 ${b.endDate} \u2014 ${b.status}`).join("\n");
|
|
22125
|
+
return { content: [{ type: "text", text }] };
|
|
22126
|
+
} catch (err) {
|
|
22127
|
+
return { content: [{ type: "text", text: `Failed to list bars: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
22115
22128
|
}
|
|
22116
|
-
const text = bars.map((b) => `${b.barId} ${b.startDate} \u2192 ${b.endDate} \u2014 ${b.status}`).join("\n");
|
|
22117
|
-
return { content: [{ type: "text", text }] };
|
|
22118
|
-
} catch (err) {
|
|
22119
|
-
return { content: [{ type: "text", text: `Failed to list bars: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
22120
22129
|
}
|
|
22121
|
-
|
|
22122
|
-
|
|
22130
|
+
);
|
|
22131
|
+
}
|
|
22123
22132
|
}
|
|
22124
22133
|
|
|
22125
22134
|
// ../../libs/harmonica-services/src/mcp/tools/baseline-tools.ts
|
|
@@ -25212,6 +25221,41 @@ var BeatDraftSuggestionSchema = external_exports.object({
|
|
|
25212
25221
|
});
|
|
25213
25222
|
var BeatDraftSuggestionsSchema = external_exports.array(BeatDraftSuggestionSchema);
|
|
25214
25223
|
|
|
25224
|
+
// ../../libs/harmonica-schemas/src/progress-evidence.ts
|
|
25225
|
+
var ProgressEvidenceQuerySchema = external_exports.object({
|
|
25226
|
+
start: external_exports.string().min(1).max(40),
|
|
25227
|
+
end: external_exports.string().min(1).max(40),
|
|
25228
|
+
timezone: external_exports.string().min(1).max(100),
|
|
25229
|
+
contributor: external_exports.string().trim().min(1).max(256).optional(),
|
|
25230
|
+
limit: external_exports.coerce.number().int().min(1).max(100).optional(),
|
|
25231
|
+
cursor: external_exports.string().min(1).max(4096).optional()
|
|
25232
|
+
}).strict();
|
|
25233
|
+
var ProgressCitationSchema = external_exports.object({ kind: external_exports.string(), id: external_exports.string(), url: external_exports.string() });
|
|
25234
|
+
var ProgressEvidenceItemSchema = external_exports.object({
|
|
25235
|
+
id: external_exports.string(),
|
|
25236
|
+
source: external_exports.enum(["activity", "session"]),
|
|
25237
|
+
occurredAt: external_exports.string(),
|
|
25238
|
+
recordedAt: external_exports.string(),
|
|
25239
|
+
summary: external_exports.string(),
|
|
25240
|
+
classification: external_exports.enum(["planned", "progressed", "delivered"]),
|
|
25241
|
+
attribution: external_exports.object({ basis: external_exports.enum(["recorded_actor", "session_initiator", "unknown"]), actorId: external_exports.string().nullable(), agentId: external_exports.string().optional() }),
|
|
25242
|
+
citations: external_exports.array(ProgressCitationSchema),
|
|
25243
|
+
context: external_exports.array(external_exports.object({ kind: external_exports.string(), id: external_exports.string(), title: external_exports.string().optional(), asOf: external_exports.literal("current") })),
|
|
25244
|
+
deliveryProof: external_exports.object({ kind: external_exports.literal("recorded_release"), occurredAt: external_exports.string(), citation: ProgressCitationSchema }).nullable()
|
|
25245
|
+
});
|
|
25246
|
+
var ProgressCoverageGapSchema = external_exports.object({ source: external_exports.string(), code: external_exports.string(), message: external_exports.string(), entityId: external_exports.string().optional() });
|
|
25247
|
+
var ProgressEvidenceResponseSchema = external_exports.object({
|
|
25248
|
+
systemId: external_exports.string(),
|
|
25249
|
+
accountId: external_exports.string().nullable(),
|
|
25250
|
+
window: external_exports.object({ start: external_exports.string(), end: external_exports.string(), timezone: external_exports.string() }),
|
|
25251
|
+
contributor: external_exports.string().nullable(),
|
|
25252
|
+
watermark: external_exports.string(),
|
|
25253
|
+
items: external_exports.array(ProgressEvidenceItemSchema),
|
|
25254
|
+
nextCursor: external_exports.string().optional(),
|
|
25255
|
+
coverage: external_exports.object({ status: external_exports.literal("partial"), gaps: external_exports.array(ProgressCoverageGapSchema) }),
|
|
25256
|
+
metrics: external_exports.object({ queryCalls: external_exports.number(), batchCalls: external_exports.number(), scannedCount: external_exports.number(), referenceCount: external_exports.number(), elapsedMs: external_exports.number() })
|
|
25257
|
+
});
|
|
25258
|
+
|
|
25215
25259
|
// ../../libs/harmonica-schemas/src/api-responses.ts
|
|
25216
25260
|
var HumanAssigneeSchema = external_exports.object({ email: external_exports.string(), name: external_exports.string() });
|
|
25217
25261
|
var AgentAssigneeSchema = external_exports.object({ agentId: external_exports.string(), name: external_exports.string() });
|
|
@@ -25913,7 +25957,11 @@ var HumanAssigneeApiSchema = external_exports.object({
|
|
|
25913
25957
|
name: external_exports.string(),
|
|
25914
25958
|
email: external_exports.string()
|
|
25915
25959
|
});
|
|
25960
|
+
var ArchiveWorkItemRequestSchema = external_exports.object({ reason: external_exports.string().trim().min(1).max(1e3) });
|
|
25916
25961
|
var WorkItemApiSchema = external_exports.object({
|
|
25962
|
+
archived: external_exports.boolean().optional(),
|
|
25963
|
+
archivedAt: external_exports.string().optional(),
|
|
25964
|
+
archivedReason: external_exports.string().optional(),
|
|
25917
25965
|
workItemId: external_exports.string(),
|
|
25918
25966
|
trackId: external_exports.string(),
|
|
25919
25967
|
teamspaceId: external_exports.string(),
|
|
@@ -25947,6 +25995,12 @@ var WorkItemApiSchema = external_exports.object({
|
|
|
25947
25995
|
* not, so a move on either side can separate the two.
|
|
25948
25996
|
*/
|
|
25949
25997
|
barId: external_exports.string().min(1).optional(),
|
|
25998
|
+
/**
|
|
25999
|
+
* Optional per-Track fractional ordering rank (N-4E09-7665). Absent when
|
|
26000
|
+
* the item is unranked — an unranked item is a first-class state. The
|
|
26001
|
+
* Work Queue uses this to order items within their displayed group.
|
|
26002
|
+
*/
|
|
26003
|
+
rank: external_exports.number().optional(),
|
|
25950
26004
|
createdBy: external_exports.string().optional(),
|
|
25951
26005
|
version: external_exports.number().optional(),
|
|
25952
26006
|
createdAt: external_exports.string(),
|
|
@@ -27017,19 +27071,19 @@ function registerMovementTools(server, _ctx, client) {
|
|
|
27017
27071
|
);
|
|
27018
27072
|
server.tool(
|
|
27019
27073
|
"create_movement",
|
|
27020
|
-
|
|
27074
|
+
`Create a Movement for an Account, in one of that Account's Teamspaces. A Movement is a client-SOW container for an engagement \u2014 it is NOT a Beat. Recognition cues: "new SOW", "create a contract scope", "start an engagement container" \u2192 create_movement.`,
|
|
27021
27075
|
{
|
|
27022
27076
|
teamspaceId: external_exports.string().describe("The teamspace ID"),
|
|
27023
27077
|
title: external_exports.string().min(1).max(300).describe('Movement title (e.g. "Q3 Platform Modernisation SOW")'),
|
|
27024
27078
|
description: external_exports.string().max(2e3).optional().describe("Optional description"),
|
|
27025
|
-
accountId: external_exports.string().min(1).max(128).
|
|
27079
|
+
accountId: external_exports.string().min(1).max(128).describe("The Account this Movement belongs to. Required (rev-950e828e). Must be in the same org as the Teamspace and linked to it.")
|
|
27026
27080
|
},
|
|
27027
27081
|
async ({ teamspaceId, title, description, accountId }) => {
|
|
27028
27082
|
const m = await client.createMovement({
|
|
27029
27083
|
teamspaceId,
|
|
27030
27084
|
title,
|
|
27031
27085
|
...description !== void 0 && { description },
|
|
27032
|
-
|
|
27086
|
+
accountId
|
|
27033
27087
|
});
|
|
27034
27088
|
return {
|
|
27035
27089
|
content: [
|
|
@@ -27048,7 +27102,7 @@ function registerMovementTools(server, _ctx, client) {
|
|
|
27048
27102
|
movementId: external_exports.string().describe("The movement ID"),
|
|
27049
27103
|
title: external_exports.string().min(1).max(300).optional().describe("New title"),
|
|
27050
27104
|
description: external_exports.string().max(2e3).optional().describe("New description"),
|
|
27051
|
-
accountId: external_exports.string().
|
|
27105
|
+
accountId: external_exports.string().min(1).max(128).optional().describe("Reassign this Movement to a different Account. Must be in the same org and linked to the teamspace. Omit to leave unchanged \u2014 a Movement cannot be left without an Account (rev-950e828e).")
|
|
27052
27106
|
},
|
|
27053
27107
|
async ({ movementId, title, description, accountId }) => {
|
|
27054
27108
|
const hasUpdates = title !== void 0 || description !== void 0 || accountId !== void 0;
|
|
@@ -27058,12 +27112,11 @@ function registerMovementTools(server, _ctx, client) {
|
|
|
27058
27112
|
isError: true
|
|
27059
27113
|
};
|
|
27060
27114
|
}
|
|
27061
|
-
const resolvedAccountId = accountId === "" ? null : accountId;
|
|
27062
27115
|
try {
|
|
27063
27116
|
const m = await client.updateMovement(movementId, {
|
|
27064
27117
|
...title !== void 0 && { title },
|
|
27065
27118
|
...description !== void 0 && { description },
|
|
27066
|
-
...accountId !== void 0 && { accountId
|
|
27119
|
+
...accountId !== void 0 && { accountId }
|
|
27067
27120
|
});
|
|
27068
27121
|
if (!m) {
|
|
27069
27122
|
return {
|
|
@@ -27071,7 +27124,7 @@ function registerMovementTools(server, _ctx, client) {
|
|
|
27071
27124
|
isError: true
|
|
27072
27125
|
};
|
|
27073
27126
|
}
|
|
27074
|
-
const account =
|
|
27127
|
+
const account = ` \u2014 Account: ${accountId ?? m.accountId}`;
|
|
27075
27128
|
return {
|
|
27076
27129
|
content: [
|
|
27077
27130
|
{
|
|
@@ -29521,6 +29574,24 @@ function registerPortfolioCoherenceTools(server, ctx, client) {
|
|
|
29521
29574
|
);
|
|
29522
29575
|
}
|
|
29523
29576
|
|
|
29577
|
+
// ../../libs/harmonica-services/src/mcp/tools/progress-evidence-tools.ts
|
|
29578
|
+
function registerProgressEvidenceTools(server, _ctx, client) {
|
|
29579
|
+
server.tool(
|
|
29580
|
+
"get_progress_evidence",
|
|
29581
|
+
"Read cited progress evidence for a system and [start,end) reporting window. Calendar dates use the required IANA timezone; timestamps need an offset. Optional contributor matches recorded actors or explicitly labeled session initiators, never assignees. Returns planned/progressed/delivered evidence, source gaps, a watermark and continuation. Follow nextCursor even on empty pages. No writes.",
|
|
29582
|
+
{ systemId: external_exports.string().min(1).max(128).regex(/^[^#]+$/), ...ProgressEvidenceQuerySchema.shape },
|
|
29583
|
+
async ({ systemId, ...query }) => {
|
|
29584
|
+
try {
|
|
29585
|
+
const report = await client.getProgressEvidence(systemId, query);
|
|
29586
|
+
if (!report) return { isError: true, content: [{ type: "text", text: "System not found." }] };
|
|
29587
|
+
return { content: [{ type: "text", text: JSON.stringify(report, null, 2) }] };
|
|
29588
|
+
} catch (error2) {
|
|
29589
|
+
return { isError: true, content: [{ type: "text", text: `Progress evidence query failed: ${error2 instanceof Error ? error2.message : String(error2)}` }] };
|
|
29590
|
+
}
|
|
29591
|
+
}
|
|
29592
|
+
);
|
|
29593
|
+
}
|
|
29594
|
+
|
|
29524
29595
|
// ../../libs/harmonica-services/src/mcp/tools/pulse-report-tools.ts
|
|
29525
29596
|
function formatBeatRow(r) {
|
|
29526
29597
|
const downbeatState = r.hasDownbeat ? "" : " \u2014 no Downbeat ever set";
|
|
@@ -31235,6 +31306,30 @@ function registerTeamspaceTools(server, ctx, client) {
|
|
|
31235
31306
|
);
|
|
31236
31307
|
}
|
|
31237
31308
|
|
|
31309
|
+
// ../../libs/harmonica-services/src/tool-schemas/track.ts
|
|
31310
|
+
var get_track = {
|
|
31311
|
+
trackId: external_exports.string().describe("The Track ID")
|
|
31312
|
+
};
|
|
31313
|
+
var update_track = {
|
|
31314
|
+
trackId: external_exports.string().describe("The Track ID"),
|
|
31315
|
+
title: external_exports.string().min(1).max(300).optional().describe("New title"),
|
|
31316
|
+
description: external_exports.string().max(2e3).optional().describe("New description"),
|
|
31317
|
+
deliverables: external_exports.string().max(2e4).optional().describe(`Replace the Track's deliverables Markdown ("" clears)`),
|
|
31318
|
+
acceptanceCriteria: external_exports.string().max(2e4).optional().describe(`Replace the Track's acceptance-criteria Markdown ("" clears)`),
|
|
31319
|
+
vuRate: external_exports.number().nonnegative().optional().describe("New VU rate")
|
|
31320
|
+
};
|
|
31321
|
+
var transition_track = {
|
|
31322
|
+
trackId: external_exports.string().describe("The Track ID"),
|
|
31323
|
+
targetState: external_exports.enum(["proposed", "approved", "active", "fulfilled", "cancelled"]).describe("Target lifecycle state"),
|
|
31324
|
+
approvalNoteId: external_exports.string().optional().describe(
|
|
31325
|
+
'Required when transitioning to "approved" \u2014 the Note ID capturing the approval decision'
|
|
31326
|
+
),
|
|
31327
|
+
reason: external_exports.string().optional().describe("Reason for the transition (especially useful for cancellation)"),
|
|
31328
|
+
confirm: external_exports.boolean().optional().default(false).describe(
|
|
31329
|
+
"false (default) = dry-run preview; true = execute the transition"
|
|
31330
|
+
)
|
|
31331
|
+
};
|
|
31332
|
+
|
|
31238
31333
|
// ../../libs/harmonica-services/src/mcp/tools/track-tools.ts
|
|
31239
31334
|
function registerTrackTools(server, ctx, client) {
|
|
31240
31335
|
server.tool(
|
|
@@ -31255,9 +31350,7 @@ function registerTrackTools(server, ctx, client) {
|
|
|
31255
31350
|
server.tool(
|
|
31256
31351
|
"get_track",
|
|
31257
31352
|
"Get a Track by ID. Returns full details including commercial lifecycle status, VU rate, and audit stamps.",
|
|
31258
|
-
|
|
31259
|
-
trackId: external_exports.string().describe("The Track ID")
|
|
31260
|
-
},
|
|
31353
|
+
get_track,
|
|
31261
31354
|
async ({ trackId }) => {
|
|
31262
31355
|
const t = await client.getTrack(trackId);
|
|
31263
31356
|
if (!t) {
|
|
@@ -31331,14 +31424,7 @@ ${t.acceptanceCriteria}` : null,
|
|
|
31331
31424
|
server.tool(
|
|
31332
31425
|
"update_track",
|
|
31333
31426
|
"Update mutable content fields on a Track (title, description, deliverables, acceptanceCriteria, vuRate). To change commercial status use transition_track instead.",
|
|
31334
|
-
|
|
31335
|
-
trackId: external_exports.string().describe("The Track ID"),
|
|
31336
|
-
title: external_exports.string().min(1).max(300).optional().describe("New title"),
|
|
31337
|
-
description: external_exports.string().max(2e3).optional().describe("New description"),
|
|
31338
|
-
deliverables: external_exports.string().max(2e4).optional().describe(`Replace the Track's deliverables Markdown ("" clears)`),
|
|
31339
|
-
acceptanceCriteria: external_exports.string().max(2e4).optional().describe(`Replace the Track's acceptance-criteria Markdown ("" clears)`),
|
|
31340
|
-
vuRate: external_exports.number().nonnegative().optional().describe("New VU rate")
|
|
31341
|
-
},
|
|
31427
|
+
update_track,
|
|
31342
31428
|
async ({ trackId, title, description, deliverables, acceptanceCriteria, vuRate }) => {
|
|
31343
31429
|
const t = await client.updateTrack(trackId, {
|
|
31344
31430
|
...title !== void 0 && { title },
|
|
@@ -31365,13 +31451,7 @@ ${t.acceptanceCriteria}` : null,
|
|
|
31365
31451
|
server.tool(
|
|
31366
31452
|
"transition_track",
|
|
31367
31453
|
'Transition a Track through its commercial lifecycle: draft \u2192 proposed \u2192 approved \u2192 active \u2192 fulfilled. Cancelled is reachable from any non-terminal state. Advancing to "approved" requires an approvalNoteId. Use confirm=false (default) to preview the transition without writing; confirm=true to execute.',
|
|
31368
|
-
|
|
31369
|
-
trackId: external_exports.string().describe("The Track ID"),
|
|
31370
|
-
targetState: external_exports.enum(["proposed", "approved", "active", "fulfilled", "cancelled"]).describe("Target lifecycle state"),
|
|
31371
|
-
approvalNoteId: external_exports.string().optional().describe('Required when transitioning to "approved" \u2014 the Note ID capturing the approval decision'),
|
|
31372
|
-
reason: external_exports.string().optional().describe("Reason for the transition (especially useful for cancellation)"),
|
|
31373
|
-
confirm: external_exports.boolean().optional().default(false).describe("false (default) = dry-run preview; true = execute the transition")
|
|
31374
|
-
},
|
|
31454
|
+
transition_track,
|
|
31375
31455
|
async ({ trackId, targetState, approvalNoteId, reason, confirm }) => {
|
|
31376
31456
|
if (!confirm) {
|
|
31377
31457
|
return {
|
|
@@ -32002,6 +32082,24 @@ function registerWorkItemTools(server, _ctx, client) {
|
|
|
32002
32082
|
);
|
|
32003
32083
|
}
|
|
32004
32084
|
|
|
32085
|
+
// ../../libs/harmonica-services/src/tool-schemas/work-order.ts
|
|
32086
|
+
var WORK_ORDER_SIZE_ENUM = ["XS", "S", "M", "L", "XL"];
|
|
32087
|
+
var create_work_order = {
|
|
32088
|
+
workRequestId: external_exports.string().min(1).describe("The Work Request this order was split from \u2014 immutable lineage"),
|
|
32089
|
+
title: external_exports.string().min(1).max(300).describe("Work order title"),
|
|
32090
|
+
description: external_exports.string().max(1e4).optional().describe("Optional detail beyond the title"),
|
|
32091
|
+
size: external_exports.enum(WORK_ORDER_SIZE_ENUM).optional().describe(
|
|
32092
|
+
"Optional t-shirt size. An order can arrive before it is sized; a request's composition counts unsized orders rather than hiding them."
|
|
32093
|
+
),
|
|
32094
|
+
createdBy: external_exports.string().optional().describe("Optional creator identity for audit")
|
|
32095
|
+
};
|
|
32096
|
+
var get_work_order = {
|
|
32097
|
+
workOrderId: external_exports.string().describe("The Work Order ID (e.g., wo-abc123)")
|
|
32098
|
+
};
|
|
32099
|
+
var list_movement_work_orders = {
|
|
32100
|
+
movementId: external_exports.string().describe("The Movement (engagement) ID")
|
|
32101
|
+
};
|
|
32102
|
+
|
|
32005
32103
|
// ../../libs/harmonica-services/src/mcp/tools/work-order-tools.ts
|
|
32006
32104
|
var WORK_INTAKE_STATUS_ENUM = [
|
|
32007
32105
|
"requested",
|
|
@@ -32014,7 +32112,7 @@ var NEEDS_DECISION_REASON_ENUM = [
|
|
|
32014
32112
|
"exceeds_capacity",
|
|
32015
32113
|
"incoherent_with_decision"
|
|
32016
32114
|
];
|
|
32017
|
-
var
|
|
32115
|
+
var WORK_ORDER_SIZE_ENUM2 = [
|
|
32018
32116
|
"XS",
|
|
32019
32117
|
"S",
|
|
32020
32118
|
"M",
|
|
@@ -32026,8 +32124,7 @@ function oneLine(value) {
|
|
|
32026
32124
|
}
|
|
32027
32125
|
function formatOrderLine(o) {
|
|
32028
32126
|
const reason = o.needsDecisionReason ? ` [${o.needsDecisionReason}]` : "";
|
|
32029
|
-
|
|
32030
|
-
return `[${o.workOrderId}] ${oneLine(o.title)} \u2014 ${o.status}${reason}, ${o.size ?? "unsized"}, ${track}`;
|
|
32127
|
+
return `[${o.workOrderId}] ${oneLine(o.title)} \u2014 ${o.status}${reason}, ${o.size ?? "unsized"}`;
|
|
32031
32128
|
}
|
|
32032
32129
|
function registerWorkOrderTools(server, _ctx, client) {
|
|
32033
32130
|
server.tool(
|
|
@@ -32057,7 +32154,7 @@ function registerWorkOrderTools(server, _ctx, client) {
|
|
|
32057
32154
|
);
|
|
32058
32155
|
server.tool(
|
|
32059
32156
|
"list_work_request_work_orders",
|
|
32060
|
-
"List the Work Orders one Work Request split into \u2014 the lineage view. A four-way split shows four orders here, each
|
|
32157
|
+
"List the Work Orders one Work Request split into \u2014 the lineage view. A four-way split shows four orders here, each with its own Items and independent state.",
|
|
32061
32158
|
{
|
|
32062
32159
|
workRequestId: external_exports.string().describe("The Work Request ID")
|
|
32063
32160
|
},
|
|
@@ -32069,10 +32166,8 @@ function registerWorkOrderTools(server, _ctx, client) {
|
|
|
32069
32166
|
);
|
|
32070
32167
|
server.tool(
|
|
32071
32168
|
"get_work_order",
|
|
32072
|
-
"Get a Work Order by ID. Returns its title, status (with the routing reason when it is awaiting a decision), t-shirt size,
|
|
32073
|
-
|
|
32074
|
-
workOrderId: external_exports.string().describe("The Work Order ID (e.g., wo-abc123)")
|
|
32075
|
-
},
|
|
32169
|
+
"Get a Work Order by ID. Returns its title, status (with the routing reason when it is awaiting a decision), t-shirt size, and the Work Request it was split from.",
|
|
32170
|
+
get_work_order,
|
|
32076
32171
|
async ({ workOrderId }) => {
|
|
32077
32172
|
const wo = await client.getWorkOrder(workOrderId);
|
|
32078
32173
|
if (!wo) {
|
|
@@ -32082,7 +32177,6 @@ function registerWorkOrderTools(server, _ctx, client) {
|
|
|
32082
32177
|
`ID: ${wo.workOrderId}`,
|
|
32083
32178
|
`Title: ${oneLine(wo.title)}`,
|
|
32084
32179
|
`From Work Request: ${wo.workRequestId}`,
|
|
32085
|
-
`Track: ${wo.trackId}`,
|
|
32086
32180
|
`Status: ${wo.status}`,
|
|
32087
32181
|
wo.needsDecisionReason ? `Routing Reason: ${wo.needsDecisionReason}` : null,
|
|
32088
32182
|
wo.size !== void 0 ? `Size: ${wo.size}` : "Size: unsized",
|
|
@@ -32094,41 +32188,37 @@ function registerWorkOrderTools(server, _ctx, client) {
|
|
|
32094
32188
|
return { content: [{ type: "text", text: lines }] };
|
|
32095
32189
|
}
|
|
32096
32190
|
);
|
|
32097
|
-
|
|
32098
|
-
|
|
32099
|
-
|
|
32100
|
-
|
|
32101
|
-
|
|
32102
|
-
|
|
32103
|
-
|
|
32104
|
-
|
|
32105
|
-
|
|
32106
|
-
|
|
32107
|
-
|
|
32108
|
-
|
|
32109
|
-
|
|
32110
|
-
|
|
32111
|
-
|
|
32112
|
-
|
|
32113
|
-
|
|
32114
|
-
|
|
32115
|
-
|
|
32116
|
-
|
|
32117
|
-
|
|
32118
|
-
|
|
32119
|
-
|
|
32120
|
-
|
|
32121
|
-
|
|
32122
|
-
|
|
32123
|
-
|
|
32124
|
-
|
|
32125
|
-
|
|
32126
|
-
|
|
32127
|
-
|
|
32128
|
-
const text = orders.length === 0 ? "No work orders found." : orders.map(formatOrderLine).join("\n");
|
|
32129
|
-
return { content: [{ type: "text", text }] };
|
|
32130
|
-
}
|
|
32131
|
-
);
|
|
32191
|
+
for (const name of ["create_work_order", "promote_work_request"]) {
|
|
32192
|
+
server.tool(
|
|
32193
|
+
name,
|
|
32194
|
+
`Create (promote) a Work Order from a Work Request. Each call creates another Order; it does not accept the Order or change Request status. Called once per piece: a request covering two separate lines of work becomes two orders. Starts in "requested" status \u2014 moving it past the coherence gate is transition_work_order_status. The Order's scope is derived from the Work Request's Movement; Tracks are assigned only to Work Items.`,
|
|
32195
|
+
create_work_order,
|
|
32196
|
+
async ({ workRequestId, title, description, size, createdBy }) => {
|
|
32197
|
+
const wo = await client.createWorkOrder({
|
|
32198
|
+
workRequestId,
|
|
32199
|
+
title,
|
|
32200
|
+
...description !== void 0 && { description },
|
|
32201
|
+
...size !== void 0 && { size },
|
|
32202
|
+
...createdBy !== void 0 && { createdBy }
|
|
32203
|
+
});
|
|
32204
|
+
return {
|
|
32205
|
+
content: [{ type: "text", text: `Created work order: ${formatOrderLine(wo)}` }]
|
|
32206
|
+
};
|
|
32207
|
+
}
|
|
32208
|
+
);
|
|
32209
|
+
}
|
|
32210
|
+
for (const name of ["list_movement_work_orders", "list_work_orders"]) {
|
|
32211
|
+
server.tool(
|
|
32212
|
+
name,
|
|
32213
|
+
'List the Work Orders belonging to a Movement (engagement). A Work Order is the internal unit of agreed work \u2014 it is split from a Work Request and scoped to the Movement that owns the Request. Recognition cues: "what is committed on this engagement", "list the orders" \u2192 list_movement_work_orders.',
|
|
32214
|
+
list_movement_work_orders,
|
|
32215
|
+
async ({ movementId }) => {
|
|
32216
|
+
const orders = await client.listMovementWorkOrders(movementId);
|
|
32217
|
+
const text = orders.length === 0 ? "No work orders found." : orders.map(formatOrderLine).join("\n");
|
|
32218
|
+
return { content: [{ type: "text", text }] };
|
|
32219
|
+
}
|
|
32220
|
+
);
|
|
32221
|
+
}
|
|
32132
32222
|
server.tool(
|
|
32133
32223
|
"update_work_order",
|
|
32134
32224
|
"Update content fields on a Work Order (title, description, size). To change its status use transition_work_order_status. There is no moving an Order between Tracks \u2014 an Order belongs to its Work Request, and a Track is attribution carried by the Work Items (set workOrderId on them via update_work_item). The Work Request it came from cannot be changed \u2014 that lineage is immutable.",
|
|
@@ -32136,7 +32226,7 @@ function registerWorkOrderTools(server, _ctx, client) {
|
|
|
32136
32226
|
workOrderId: external_exports.string().describe("The Work Order ID"),
|
|
32137
32227
|
title: external_exports.string().min(1).max(300).optional().describe("New title"),
|
|
32138
32228
|
description: external_exports.string().max(1e4).nullable().optional().describe("New description. Pass null to clear; omit to leave untouched."),
|
|
32139
|
-
size: external_exports.enum(
|
|
32229
|
+
size: external_exports.enum(WORK_ORDER_SIZE_ENUM2).nullable().optional().describe("New t-shirt size. Pass null to return it to unsized; omit to leave untouched.")
|
|
32140
32230
|
},
|
|
32141
32231
|
async ({ workOrderId, title, description, size }) => {
|
|
32142
32232
|
if (title === void 0 && description === void 0 && size === void 0) {
|
|
@@ -32225,11 +32315,27 @@ function registerWorkOrderTools(server, _ctx, client) {
|
|
|
32225
32315
|
);
|
|
32226
32316
|
}
|
|
32227
32317
|
|
|
32318
|
+
// ../../libs/harmonica-services/src/tool-schemas/work-request.ts
|
|
32319
|
+
var WORK_REQUEST_ORIGIN_ENUM = ["client", "internal"];
|
|
32320
|
+
var create_work_request = {
|
|
32321
|
+
movementId: external_exports.string().describe("The Movement (engagement) this request is filed against"),
|
|
32322
|
+
title: external_exports.string().min(1).max(300).describe("Short title for the ask"),
|
|
32323
|
+
body: external_exports.string().max(1e4).optional().describe("The ask as written, in the requester's own words"),
|
|
32324
|
+
requesterName: external_exports.string().min(1).describe("Who is asking \u2014 display name"),
|
|
32325
|
+
requesterEmail: external_exports.string().email().describe("Who is asking \u2014 email"),
|
|
32326
|
+
origin: external_exports.enum(WORK_REQUEST_ORIGIN_ENUM).describe(
|
|
32327
|
+
'Where the ask came from: "client" or "internal". One entity covers both \u2014 only the entry point differs.'
|
|
32328
|
+
),
|
|
32329
|
+
createdBy: external_exports.string().optional().describe("Optional creator identity for audit")
|
|
32330
|
+
};
|
|
32331
|
+
var get_work_request = {
|
|
32332
|
+
workRequestId: external_exports.string().describe("The Work Request ID (e.g., wr-abc123)")
|
|
32333
|
+
};
|
|
32334
|
+
var list_movement_work_requests = {
|
|
32335
|
+
movementId: external_exports.string().describe("The Movement (engagement) ID")
|
|
32336
|
+
};
|
|
32337
|
+
|
|
32228
32338
|
// ../../libs/harmonica-services/src/mcp/tools/work-request-tools.ts
|
|
32229
|
-
var WORK_REQUEST_ORIGIN_ENUM = [
|
|
32230
|
-
"client",
|
|
32231
|
-
"internal"
|
|
32232
|
-
];
|
|
32233
32339
|
function oneLine2(value) {
|
|
32234
32340
|
return value.replace(/[\r\n]+/g, " ");
|
|
32235
32341
|
}
|
|
@@ -32243,9 +32349,7 @@ function registerWorkRequestTools(server, _ctx, client) {
|
|
|
32243
32349
|
server.tool(
|
|
32244
32350
|
"list_movement_work_requests",
|
|
32245
32351
|
`List the Work Requests filed against a Movement. A Work Request is the customer's ask in their own language, before it is sized or agreed. Recognition cues: "what has the client asked for", "list the requests on this engagement" \u2192 list_movement_work_requests.`,
|
|
32246
|
-
|
|
32247
|
-
movementId: external_exports.string().describe("The Movement (engagement) ID")
|
|
32248
|
-
},
|
|
32352
|
+
list_movement_work_requests,
|
|
32249
32353
|
async ({ movementId }) => {
|
|
32250
32354
|
const requests = await client.listMovementWorkRequests(movementId);
|
|
32251
32355
|
const text = requests.length === 0 ? "No work requests found." : requests.map((wr) => `[${wr.workRequestId}] ${oneLine2(wr.title)} \u2014 from ${oneLine2(wr.requester.name)} (${wr.origin})`).join("\n");
|
|
@@ -32255,9 +32359,7 @@ function registerWorkRequestTools(server, _ctx, client) {
|
|
|
32255
32359
|
server.tool(
|
|
32256
32360
|
"get_work_request",
|
|
32257
32361
|
"Get a Work Request with the Work Orders it split into and their combined size composition. Note there is no request-level status: each Order carries its own, because a split whose halves sit in different states cannot be reduced to one word.",
|
|
32258
|
-
|
|
32259
|
-
workRequestId: external_exports.string().describe("The Work Request ID (e.g., wr-abc123)")
|
|
32260
|
-
},
|
|
32362
|
+
get_work_request,
|
|
32261
32363
|
async ({ workRequestId }) => {
|
|
32262
32364
|
const detail = await client.getWorkRequestDetail(workRequestId);
|
|
32263
32365
|
if (!detail) {
|
|
@@ -32267,10 +32369,9 @@ function registerWorkRequestTools(server, _ctx, client) {
|
|
|
32267
32369
|
const orderLines = orders.length === 0 ? ["Orders: none yet \u2014 this request has not been split"] : [
|
|
32268
32370
|
"Orders:",
|
|
32269
32371
|
...orders.map((o) => {
|
|
32270
|
-
const track = o.trackTitle ? `${oneLine2(o.trackTitle)} (${o.trackId})` : o.trackId;
|
|
32271
32372
|
const reason = o.needsDecisionReason ? ` [${o.needsDecisionReason}]` : "";
|
|
32272
32373
|
const sized = o.size ?? "unsized";
|
|
32273
|
-
return ` [${o.workOrderId}] ${oneLine2(o.title)} \u2014 ${o.status}${reason}, ${sized}
|
|
32374
|
+
return ` [${o.workOrderId}] ${oneLine2(o.title)} \u2014 ${o.status}${reason}, ${sized}`;
|
|
32274
32375
|
})
|
|
32275
32376
|
];
|
|
32276
32377
|
const lines = [
|
|
@@ -32292,15 +32393,7 @@ function registerWorkRequestTools(server, _ctx, client) {
|
|
|
32292
32393
|
server.tool(
|
|
32293
32394
|
"create_work_request",
|
|
32294
32395
|
"File a Work Request against a Movement \u2014 how work arrives, in the requester's own language, before it is sized or agreed. Splitting it into Work Orders is a separate step (create_work_order).",
|
|
32295
|
-
|
|
32296
|
-
movementId: external_exports.string().describe("The Movement (engagement) this request is filed against"),
|
|
32297
|
-
title: external_exports.string().min(1).max(300).describe("Short title for the ask"),
|
|
32298
|
-
body: external_exports.string().max(1e4).optional().describe("The ask as written, in the requester's own words"),
|
|
32299
|
-
requesterName: external_exports.string().min(1).describe("Who is asking \u2014 display name"),
|
|
32300
|
-
requesterEmail: external_exports.string().email().describe("Who is asking \u2014 email"),
|
|
32301
|
-
origin: external_exports.enum(WORK_REQUEST_ORIGIN_ENUM).describe('Where the ask came from: "client" or "internal". One entity covers both \u2014 only the entry point differs.'),
|
|
32302
|
-
createdBy: external_exports.string().optional().describe("Optional creator identity for audit")
|
|
32303
|
-
},
|
|
32396
|
+
create_work_request,
|
|
32304
32397
|
async ({ movementId, title, body, requesterName, requesterEmail, origin, createdBy }) => {
|
|
32305
32398
|
const wr = await client.createWorkRequest({
|
|
32306
32399
|
movementId,
|
|
@@ -32858,6 +32951,7 @@ function registerAllTools(server, ctx, client, profile) {
|
|
|
32858
32951
|
registerNextActionsTools(server, ctx, client);
|
|
32859
32952
|
registerCheckTools(server, ctx, client);
|
|
32860
32953
|
registerActivityTools(server, ctx, client);
|
|
32954
|
+
registerProgressEvidenceTools(server, ctx, client);
|
|
32861
32955
|
registerAuditIntegrityTools(server, ctx, client);
|
|
32862
32956
|
registerAnalysisTools(server, ctx, client);
|
|
32863
32957
|
registerOrganizationTools(server, ctx, client);
|
|
@@ -34523,10 +34617,13 @@ function createHttpClient(config2) {
|
|
|
34523
34617
|
const { teamspaceId, title, description, accountId } = input;
|
|
34524
34618
|
const body = {
|
|
34525
34619
|
title,
|
|
34526
|
-
...description !== void 0 && { description }
|
|
34527
|
-
...accountId !== void 0 && { accountId }
|
|
34620
|
+
...description !== void 0 && { description }
|
|
34528
34621
|
};
|
|
34529
|
-
const result = await request(
|
|
34622
|
+
const result = await request(
|
|
34623
|
+
"POST",
|
|
34624
|
+
`/api/teamspaces/${encodeURIComponent(teamspaceId)}/accounts/${encodeURIComponent(accountId)}/movements`,
|
|
34625
|
+
body
|
|
34626
|
+
);
|
|
34530
34627
|
if (result === void 0) throw new Error("Resource not found when creating Movement");
|
|
34531
34628
|
const parsed = MovementApiSchema.safeParse(result);
|
|
34532
34629
|
if (!parsed.success) throw new Error(`Unexpected Movement shape from API: ${parsed.error.message}`);
|
|
@@ -34936,6 +35033,11 @@ function createHttpClient(config2) {
|
|
|
34936
35033
|
const qs = asOf ? `?asOf=${encodeURIComponent(asOf)}` : "";
|
|
34937
35034
|
return request("GET", `/api/accounts/${encodeURIComponent(accountId)}/cadence-schedule${qs}`);
|
|
34938
35035
|
},
|
|
35036
|
+
getProgressEvidence: async (systemId, query) => {
|
|
35037
|
+
const qs = new URLSearchParams();
|
|
35038
|
+
for (const [key, value] of Object.entries(query)) if (value !== void 0) qs.set(key, String(value));
|
|
35039
|
+
return request("GET", `/api/systems/${encodeURIComponent(systemId)}/progress-evidence?${qs}`);
|
|
35040
|
+
},
|
|
34939
35041
|
getPulseReport: async (accountId, asOf) => {
|
|
34940
35042
|
const qs = asOf ? `?asOf=${encodeURIComponent(asOf)}` : "";
|
|
34941
35043
|
return request("GET", `/api/accounts/${encodeURIComponent(accountId)}/pulse-report${qs}`);
|
|
@@ -35369,7 +35471,7 @@ function loadConfig() {
|
|
|
35369
35471
|
};
|
|
35370
35472
|
}
|
|
35371
35473
|
async function main() {
|
|
35372
|
-
console.error(`[harmonica-mcp] v${"5.
|
|
35474
|
+
console.error(`[harmonica-mcp] v${"5.1.0"} starting\u2026`);
|
|
35373
35475
|
const config2 = loadConfig();
|
|
35374
35476
|
const client = createHttpClient({
|
|
35375
35477
|
apiBaseUrl: config2.apiBaseUrl,
|