@letpeoplework/lighthouse-mcp-core 1.8.1 → 1.10.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.cjs +127 -8
- package/dist/index.d.cts +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +128 -9
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -426,6 +426,119 @@ const REFINEMENT_TOOLS = /* @__PURE__ */ new Map([
|
|
|
426
426
|
/** The handler of a refinement tool, or undefined for a tool that is not one. */
|
|
427
427
|
const findRefinementTool = (name) => REFINEMENT_TOOLS.get(name);
|
|
428
428
|
//#endregion
|
|
429
|
+
//#region src/reportTools.ts
|
|
430
|
+
const HOW_LIGHTHOUSE_SHOWS_IT = "Read the lighthouse-reports skill for how Lighthouse shows these values, and quote them as they come.";
|
|
431
|
+
const HOW_THE_ANSWER_READS = "The first block is Lighthouse's answer, unchanged; a second text block, `summary`,";
|
|
432
|
+
const LIST_LABEL = "reports";
|
|
433
|
+
const GET_LABEL = "report";
|
|
434
|
+
const NEEDS_A_SERVER = "Needs a Lighthouse newer than v26.10.10.1.";
|
|
435
|
+
const ownerIdProperty = (kind) => ({
|
|
436
|
+
type: "integer",
|
|
437
|
+
description: `The ${kind}'s numeric id.`
|
|
438
|
+
});
|
|
439
|
+
const reportIdProperty = {
|
|
440
|
+
type: "integer",
|
|
441
|
+
description: "The report's id, as its owner's report list names it under reports[].id."
|
|
442
|
+
};
|
|
443
|
+
const listInputSchema = (kind) => ({
|
|
444
|
+
type: "object",
|
|
445
|
+
properties: { id: ownerIdProperty(kind) },
|
|
446
|
+
required: ["id"],
|
|
447
|
+
additionalProperties: false
|
|
448
|
+
});
|
|
449
|
+
const getInputSchema = (kind) => ({
|
|
450
|
+
type: "object",
|
|
451
|
+
properties: {
|
|
452
|
+
id: ownerIdProperty(kind),
|
|
453
|
+
reportId: reportIdProperty
|
|
454
|
+
},
|
|
455
|
+
required: ["id", "reportId"],
|
|
456
|
+
additionalProperties: false
|
|
457
|
+
});
|
|
458
|
+
const listDescription = (kind) => `List the reports a ${kind} holds by ${kind} ID: each report's id, name, type (templateKey) and when it was created. ${HOW_THE_ANSWER_READS} names the ${kind} and counts its reports in the instance's terminology. ${HOW_LIGHTHOUSE_SHOWS_IT} ${NEEDS_A_SERVER}`;
|
|
459
|
+
const getDescription = (kind) => `Get one of a ${kind}'s reports by ${kind} ID and reportId, exactly as Lighthouse answers it: its Then side as Lighthouse froze it, its Now side read afresh up to today, and every value, a missing value with its reason. ${HOW_LIGHTHOUSE_SHOWS_IT} ${HOW_THE_ANSWER_READS} names the report, its ${kind} and its Then and Now windows. ${NEEDS_A_SERVER}`;
|
|
460
|
+
const reportToolDefinitions = [
|
|
461
|
+
{
|
|
462
|
+
name: "lighthouse_team_report_list",
|
|
463
|
+
description: listDescription("team"),
|
|
464
|
+
inputSchema: listInputSchema("team")
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
name: "lighthouse_team_report_get",
|
|
468
|
+
description: getDescription("team"),
|
|
469
|
+
inputSchema: getInputSchema("team")
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
name: "lighthouse_portfolio_report_list",
|
|
473
|
+
description: listDescription("portfolio"),
|
|
474
|
+
inputSchema: listInputSchema("portfolio")
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: "lighthouse_portfolio_report_get",
|
|
478
|
+
description: getDescription("portfolio"),
|
|
479
|
+
inputSchema: getInputSchema("portfolio")
|
|
480
|
+
}
|
|
481
|
+
];
|
|
482
|
+
const listSchema = zod.z.object({ id: zod.z.number().int() });
|
|
483
|
+
const getSchema = zod.z.object({
|
|
484
|
+
id: zod.z.number().int(),
|
|
485
|
+
reportId: zod.z.number().int()
|
|
486
|
+
});
|
|
487
|
+
const reportInputSchemas = {
|
|
488
|
+
lighthouse_team_report_list: listSchema,
|
|
489
|
+
lighthouse_team_report_get: getSchema,
|
|
490
|
+
lighthouse_portfolio_report_list: listSchema,
|
|
491
|
+
lighthouse_portfolio_report_get: getSchema
|
|
492
|
+
};
|
|
493
|
+
const getReportId = (argumentsPayload) => {
|
|
494
|
+
const reportId = typeof argumentsPayload === "object" && argumentsPayload !== null ? argumentsPayload.reportId : void 0;
|
|
495
|
+
return typeof reportId === "number" && Number.isInteger(reportId) ? reportId : null;
|
|
496
|
+
};
|
|
497
|
+
const answerReportRead = async (client, owner, result, answer) => {
|
|
498
|
+
if (!result.ok) return getErrorToolResult(`${answer.label}: ${await (0, _letpeoplework_lighthouse_client.readReportRefusal)(client, result.error, owner.kind, answer.read)}`);
|
|
499
|
+
const ownerName = await (0, _letpeoplework_lighthouse_client.readReportOwner)(client, owner);
|
|
500
|
+
return withSummaryBlock(`${answer.label}: ${encodePayload(result.value)}`, answer.summarise(result.value, ownerName));
|
|
501
|
+
};
|
|
502
|
+
const invalid = (label, what) => getErrorToolResult(`${label}: invalid ${what}`);
|
|
503
|
+
const reportList = (kind) => async (argumentsPayload, client) => {
|
|
504
|
+
const id = getNumericId(argumentsPayload);
|
|
505
|
+
if (id === null) return invalid(LIST_LABEL, "id");
|
|
506
|
+
const owner = {
|
|
507
|
+
kind,
|
|
508
|
+
id
|
|
509
|
+
};
|
|
510
|
+
return answerReportRead(client, owner, await (0, _letpeoplework_lighthouse_client.readOwnersReports)(client, owner), {
|
|
511
|
+
label: LIST_LABEL,
|
|
512
|
+
read: "list",
|
|
513
|
+
summarise: _letpeoplework_lighthouse_client.summariseReportList
|
|
514
|
+
});
|
|
515
|
+
};
|
|
516
|
+
const reportGet = (kind) => async (argumentsPayload, client) => {
|
|
517
|
+
const id = getNumericId(argumentsPayload);
|
|
518
|
+
if (id === null) return invalid(GET_LABEL, "id");
|
|
519
|
+
const reportId = getReportId(argumentsPayload);
|
|
520
|
+
if (reportId === null) return invalid(GET_LABEL, "reportId");
|
|
521
|
+
const owner = {
|
|
522
|
+
kind,
|
|
523
|
+
id
|
|
524
|
+
};
|
|
525
|
+
const result = await (0, _letpeoplework_lighthouse_client.readOwnersReport)(client, owner, reportId);
|
|
526
|
+
const answered = await answerReportRead(client, owner, result, {
|
|
527
|
+
label: GET_LABEL,
|
|
528
|
+
read: "report",
|
|
529
|
+
summarise: _letpeoplework_lighthouse_client.summariseReport
|
|
530
|
+
});
|
|
531
|
+
return countedToolResult(answered, (0, _letpeoplework_lighthouse_client.reportOpenedOccurrencesOf)(result));
|
|
532
|
+
};
|
|
533
|
+
const REPORT_TOOLS = /* @__PURE__ */ new Map([
|
|
534
|
+
["lighthouse_team_report_list", reportList("team")],
|
|
535
|
+
["lighthouse_team_report_get", reportGet("team")],
|
|
536
|
+
["lighthouse_portfolio_report_list", reportList("portfolio")],
|
|
537
|
+
["lighthouse_portfolio_report_get", reportGet("portfolio")]
|
|
538
|
+
]);
|
|
539
|
+
/** The handler of one of the report tools, or undefined for any other tool. */
|
|
540
|
+
const findReportTool = (name) => REPORT_TOOLS.get(name);
|
|
541
|
+
//#endregion
|
|
429
542
|
//#region src/index.ts
|
|
430
543
|
const getMcpCorePackageContract = () => ({
|
|
431
544
|
name: "@letpeoplework/lighthouse-mcp-core",
|
|
@@ -561,6 +674,7 @@ const recurringBlackoutRuleProperties = {
|
|
|
561
674
|
description: "Human-readable description of the rule."
|
|
562
675
|
}
|
|
563
676
|
};
|
|
677
|
+
const BEYOND_HORIZON_NOTE = "A forecast percentile with beyondHorizon true lies beyond Lighthouse's simulation horizon of horizonYears years: its expectedDate is only a stand-in, not a forecast, so say \"More than <horizonYears> years\" and never that date. Both fields are absent on an older Lighthouse, whose dates stand as sent.";
|
|
564
678
|
const toolDefinitions = [
|
|
565
679
|
{
|
|
566
680
|
name: "lighthouse_health_check",
|
|
@@ -690,7 +804,7 @@ const toolDefinitions = [
|
|
|
690
804
|
},
|
|
691
805
|
{
|
|
692
806
|
name: "lighthouse_portfolio_metrics_blockedCountHistory",
|
|
693
|
-
description: "Get the blocked-items-over-time trend for a portfolio by ID: how many work items were blocked on each captured day, optionally filtered by start and end dates. No tool reads what is blocked right now for a portfolio: name the command `lh metrics portfolio --metrics wip --id <id>`, whose items each carry isBlocked and, when blocked, a blockedSince timestamp. A second text block, `summary: …`, states the answer as the dashboard does, in the instance's terminology: the heading and its sentence; the first block is the facts, unchanged.",
|
|
807
|
+
description: "Get the blocked-items-over-time trend for a portfolio by ID: how many work items were blocked on each captured day, optionally filtered by start and end dates. No tool reads what is blocked right now for a portfolio: name the command `lh metrics portfolio --metrics wip --id <id>`, whose items each carry isBlocked and, when blocked, a blockedSince timestamp, and whether Lighthouse marks it stale (isStale) and why (staleReasons), both absent on an older Lighthouse. A second text block, `summary: …`, states the answer as the dashboard does, in the instance's terminology: the heading and its sentence; the first block is the facts, unchanged.",
|
|
694
808
|
inputSchema: {
|
|
695
809
|
type: "object",
|
|
696
810
|
properties: {
|
|
@@ -824,7 +938,7 @@ const toolDefinitions = [
|
|
|
824
938
|
},
|
|
825
939
|
{
|
|
826
940
|
name: "lighthouse_feature_get",
|
|
827
|
-
description: "Get feature details by numeric IDs or external reference IDs. A second text block, `summary`, counts them in the instance's terminology; the first block is the facts, unchanged.",
|
|
941
|
+
description: "Get feature details by numeric IDs or external reference IDs. A second text block, `summary`, counts them in the instance's terminology; the first block is the facts, unchanged. " + BEYOND_HORIZON_NOTE,
|
|
828
942
|
inputSchema: {
|
|
829
943
|
type: "object",
|
|
830
944
|
properties: {
|
|
@@ -849,12 +963,12 @@ const toolDefinitions = [
|
|
|
849
963
|
},
|
|
850
964
|
{
|
|
851
965
|
name: "lighthouse_delivery_list",
|
|
852
|
-
description: "List deliveries for a portfolio by portfolio ID. The answer holds the running deliveries under `active` and the archived ones under `archived`; a `summary` field counts the running ones in the instance's terminology.",
|
|
966
|
+
description: "List deliveries for a portfolio by portfolio ID. The answer holds the running deliveries under `active` and the archived ones under `archived`; a `summary` field counts the running ones in the instance's terminology. " + BEYOND_HORIZON_NOTE,
|
|
853
967
|
inputSchema: idInputSchema
|
|
854
968
|
},
|
|
855
969
|
{
|
|
856
970
|
name: "lighthouse_delivery_metrics",
|
|
857
|
-
description: "Get a delivery's recorded trend by delivery ID: one row per day with total, done and remaining work, the epic count and the likelihood. Set detail to \"epics\" for the per-epic breakdown and the forecast distribution, which are far larger. Forward-only, so it starts at the first recorded snapshot. `summary` states the delivery and its date as lh heads them, in the instance's terminology: a second text block beside the rows, a field of the detailed payload. Requires Lighthouse newer than v26.5.29.5.",
|
|
971
|
+
description: "Get a delivery's recorded trend by delivery ID: one row per day with total, done and remaining work, the epic count and the likelihood. Set detail to \"epics\" for the per-epic breakdown and the forecast distribution, which are far larger. Forward-only, so it starts at the first recorded snapshot. `summary` states the delivery and its date as lh heads them, in the instance's terminology: a second text block beside the rows, a field of the detailed payload. Requires Lighthouse newer than v26.5.29.5. " + BEYOND_HORIZON_NOTE,
|
|
858
972
|
inputSchema: {
|
|
859
973
|
type: "object",
|
|
860
974
|
properties: {
|
|
@@ -918,7 +1032,7 @@ const toolDefinitions = [
|
|
|
918
1032
|
},
|
|
919
1033
|
{
|
|
920
1034
|
name: "lighthouse_forecast_manual",
|
|
921
|
-
description: "Run a manual forecast for a team by ID with optional remaining items and target date. Pass applyFilterOverride=true to apply the team's forecast filter, false to skip it, or omit to respect the team setting (Lighthouse v26.5.24.10+). The response includes filterApplied (boolean) and excludedSummary (string) when a filter was applied. `summary` states the answer as the web does, in the instance's terminology: the heading, then the likelihood sentence when both remaining items and a target date were given.",
|
|
1035
|
+
description: "Run a manual forecast for a team by ID with optional remaining items and target date. Pass applyFilterOverride=true to apply the team's forecast filter, false to skip it, or omit to respect the team setting (Lighthouse v26.5.24.10+). The response includes filterApplied (boolean) and excludedSummary (string) when a filter was applied. `summary` states the answer as the web does, in the instance's terminology: the heading, then the likelihood sentence when both remaining items and a target date were given. " + BEYOND_HORIZON_NOTE,
|
|
922
1036
|
inputSchema: {
|
|
923
1037
|
type: "object",
|
|
924
1038
|
properties: {
|
|
@@ -1058,7 +1172,7 @@ const toolDefinitions = [
|
|
|
1058
1172
|
},
|
|
1059
1173
|
{
|
|
1060
1174
|
name: "lighthouse_team_metrics_wip",
|
|
1061
|
-
description: "Get what a team has in progress right now by ID: each work item in progress today with its age (workItemAge), state, whether it is blocked (isBlocked) and since when (blockedSince), and its link (url). Use it for what is blocked or aging today; for how many were blocked on past days use lighthouse_team_metrics_blockedCountHistory. A second text block, `summary: …`, states the answer as lh does, in the instance's terminology: the heading, how many are in progress against the System WIP Limit, and how many are
|
|
1175
|
+
description: "Get what a team has in progress right now by ID: each work item in progress today with its age (workItemAge), state, whether it is blocked (isBlocked) and since when (blockedSince), whether Lighthouse marks it stale (isStale) and why (staleReasons: each a kind, TimeInState, BlockedDuration or TimeInStateWhileBlocked, with its days, and the state's stateName on the two time-in-state kinds), and its link (url). isStale and staleReasons are absent on an older Lighthouse, which does not say which work items are stale; never work staleness out yourself. Use it for what is blocked, stale or aging today; for how many were blocked on past days use lighthouse_team_metrics_blockedCountHistory. A second text block, `summary: …`, states the answer as lh does, in the instance's terminology: the heading, how many are in progress against the System WIP Limit, how many are blocked and how many are stale; the first block is the facts, unchanged.",
|
|
1062
1176
|
inputSchema: idInputSchema
|
|
1063
1177
|
},
|
|
1064
1178
|
{
|
|
@@ -1075,7 +1189,8 @@ const toolDefinitions = [
|
|
|
1075
1189
|
name: "lighthouse_portfolio_metrics_processBehaviorChart",
|
|
1076
1190
|
description: "Get one Process Behaviour Chart of a portfolio by ID, as Lighthouse computes it: its status (and statusReason when it is not Ready), the average and natural process limits, whether a baseline is configured, and every point (xValue, yValue) with the signals Lighthouse found on it (specialCauses: LargeChange, ModerateChange, ModerateShift, SmallShift; None means no signal) and whether it falls on a blackout day (isBlackout). metricType picks the chart: Throughput, Arrivals, Wip, WorkItemAge (the Total Work Item Age chart), CycleTime or FeatureSize. A point is a day, except on CycleTime, which has one point per finished work item, and FeatureSize, which has one per finished feature, each dated when it finished, so several can share a day. Use it for \"is something unusual going on\" and \"did our process change\"; never work a signal out yourself. Optionally filtered by start and end dates. A second text block, `summary: …`, states the answer as lh does, in the instance's terminology: the heading, the chart's title, and each signal with the days it fired, or that there are no signals; the facts are unchanged.",
|
|
1077
1191
|
inputSchema: chartInputSchema(processBehaviorMetricTypes)
|
|
1078
|
-
}
|
|
1192
|
+
},
|
|
1193
|
+
...reportToolDefinitions
|
|
1079
1194
|
];
|
|
1080
1195
|
const getDefinitionId = (argumentsPayload) => {
|
|
1081
1196
|
if (typeof argumentsPayload !== "object" || argumentsPayload === null || Array.isArray(argumentsPayload)) return;
|
|
@@ -1197,7 +1312,8 @@ const summariseCurrentWip = (today) => (facts, known) => {
|
|
|
1197
1312
|
if (now === null) return null;
|
|
1198
1313
|
const { terms } = known.wording;
|
|
1199
1314
|
const blocked = (0, _letpeoplework_lighthouse_client.describeBlockedNow)(now, "team", terms);
|
|
1200
|
-
|
|
1315
|
+
const stale = (0, _letpeoplework_lighthouse_client.describeStaleNow)(now, "team", terms);
|
|
1316
|
+
return linesOf((0, _letpeoplework_lighthouse_client.describeAsOfHeading)({ endDate: today }, known.wording), sentenceOf((0, _letpeoplework_lighthouse_client.describeInProgressNow)(now, "team", terms, known.systemWipLimit ?? void 0)), blocked === null ? null : sentenceOf(blocked), stale === null ? null : sentenceOf(stale), ...(0, _letpeoplework_lighthouse_client.describeWhatWipLeavesUnsaid)(now, "team", terms, known.systemWipLimit));
|
|
1201
1317
|
};
|
|
1202
1318
|
const readTeamForSummary = async (client, teamId) => {
|
|
1203
1319
|
const team = client.getTeam(teamId);
|
|
@@ -1361,6 +1477,7 @@ const toolInputSchemas = {
|
|
|
1361
1477
|
lighthouse_team_refresh: zod.z.object({ id: zod.z.number().int() }),
|
|
1362
1478
|
lighthouse_team_refinement_get: zod.z.object({ id: zod.z.number().int() }),
|
|
1363
1479
|
...refinementWriteInputSchemas,
|
|
1480
|
+
...reportInputSchemas,
|
|
1364
1481
|
lighthouse_portfolio_list: zod.z.object({}),
|
|
1365
1482
|
lighthouse_portfolio_get: zod.z.object({ id: zod.z.number().int() }),
|
|
1366
1483
|
lighthouse_portfolio_refresh: zod.z.object({ id: zod.z.number().int() }),
|
|
@@ -1587,6 +1704,8 @@ const answerToolCall = (dependencies) => async (name, argumentsPayload) => {
|
|
|
1587
1704
|
}
|
|
1588
1705
|
const refinementTool = findRefinementTool(name);
|
|
1589
1706
|
if (refinementTool !== void 0) return refinementTool(argumentsPayload, client, dependencies);
|
|
1707
|
+
const reportTool = findReportTool(name);
|
|
1708
|
+
if (reportTool !== void 0) return reportTool(argumentsPayload, client);
|
|
1590
1709
|
if (name === "lighthouse_portfolio_list") return answerOwnerList("portfolio", "portfolios", client.listPortfolios(), client);
|
|
1591
1710
|
if (name === "lighthouse_portfolio_get") {
|
|
1592
1711
|
const id = getNumericId(argumentsPayload);
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeliveryMetricsHistory, LighthouseApiResult, LighthouseClient, LighthouseVoterKeyStore, PortfolioDeliveries, RefinementMomentFacts, RefinementWordingSource, TeamRefinement, UsageDataOccurrence, UsageDataQuestionAnswer, UsageDataStep, VotedRow } from "@letpeoplework/lighthouse-client";
|
|
1
|
+
import { DeliveryMetricsHistory, LighthouseApiResult, LighthouseClient, LighthouseVoterKeyStore, PortfolioDeliveries, RefinementMomentFacts, RefinementWordingSource, ReportOwnerSource, ReportReads, TeamRefinement, UsageDataOccurrence, UsageDataQuestionAnswer, UsageDataStep, VotedRow } from "@letpeoplework/lighthouse-client";
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import "zod";
|
|
4
4
|
import { ElicitRequestFormParams, ElicitResult } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -75,6 +75,9 @@ type ToolError = {
|
|
|
75
75
|
};
|
|
76
76
|
type RefinementToolClient = RefinementWordingSource<ToolError> & Pick<LighthouseClient, "getTeamRefinement" | "castRefinementVote" | "addRefinementComment" | "takeBackRefinementVote" | "getRefinementLog" | "getTerminology">;
|
|
77
77
|
//#endregion
|
|
78
|
+
//#region src/reportTools.d.ts
|
|
79
|
+
type ReportToolClient = ReportReads & ReportOwnerSource;
|
|
80
|
+
//#endregion
|
|
78
81
|
//#region src/index.d.ts
|
|
79
82
|
export type McpCorePackageContract = {
|
|
80
83
|
readonly name: "@letpeoplework/lighthouse-mcp-core";
|
|
@@ -83,7 +86,7 @@ export type McpCorePackageContract = {
|
|
|
83
86
|
};
|
|
84
87
|
export declare const getMcpCorePackageContract: () => McpCorePackageContract;
|
|
85
88
|
export type McpToolDefinition = {
|
|
86
|
-
readonly name: "lighthouse_health_check" | "lighthouse_version_get" | "lighthouse_worktracking_list" | "lighthouse_worktracking_get" | "lighthouse_team_list" | "lighthouse_team_get" | "lighthouse_team_refresh" | "lighthouse_team_refinement_get" | "lighthouse_team_refinement_vote" | "lighthouse_team_refinement_comment" | "lighthouse_team_refinement_voteTakeBack" | "lighthouse_portfolio_list" | "lighthouse_portfolio_get" | "lighthouse_portfolio_refresh" | "lighthouse_team_metrics_throughput" | "lighthouse_team_metrics_cycleTimePercentiles" | "lighthouse_team_metrics_workItemAgePercentiles" | "lighthouse_portfolio_metrics_workItemAgePercentiles" | "lighthouse_team_metrics_workItemAge" | "lighthouse_team_metrics_totalWorkItemAge" | "lighthouse_portfolio_metrics_throughput" | "lighthouse_portfolio_metrics_workItemAge" | "lighthouse_portfolio_metrics_totalWorkItemAge" | "lighthouse_team_metrics_blockedCountHistory" | "lighthouse_portfolio_metrics_blockedCountHistory" | "lighthouse_team_metrics_wip" | "lighthouse_team_metrics_sleRisk" | "lighthouse_team_metrics_processBehaviorChart" | "lighthouse_portfolio_metrics_processBehaviorChart" | "lighthouse_team_metrics_percentilesOverTime" | "lighthouse_portfolio_metrics_percentilesOverTime" | "lighthouse_team_metrics_processBehaviorOverTime" | "lighthouse_portfolio_metrics_processBehaviorOverTime" | "lighthouse_team_metrics_cumulativeStateTime" | "lighthouse_team_metrics_cumulativeStateTimeItems" | "lighthouse_team_metrics_cumulativeStateTimeCandidates" | "lighthouse_portfolio_metrics_cumulativeStateTime" | "lighthouse_portfolio_metrics_cumulativeStateTimeItems" | "lighthouse_portfolio_metrics_cumulativeStateTimeCandidates" | "lighthouse_feature_get" | "lighthouse_feature_workitems" | "lighthouse_delivery_list" | "lighthouse_delivery_metrics" | "lighthouse_blackout_list" | "lighthouse_blackout_create" | "lighthouse_blackout_update" | "lighthouse_blackout_delete" | "lighthouse_forecast_manual" | "lighthouse_forecast_backtest";
|
|
89
|
+
readonly name: "lighthouse_health_check" | "lighthouse_version_get" | "lighthouse_worktracking_list" | "lighthouse_worktracking_get" | "lighthouse_team_list" | "lighthouse_team_get" | "lighthouse_team_refresh" | "lighthouse_team_refinement_get" | "lighthouse_team_refinement_vote" | "lighthouse_team_refinement_comment" | "lighthouse_team_refinement_voteTakeBack" | "lighthouse_portfolio_list" | "lighthouse_portfolio_get" | "lighthouse_portfolio_refresh" | "lighthouse_team_metrics_throughput" | "lighthouse_team_metrics_cycleTimePercentiles" | "lighthouse_team_metrics_workItemAgePercentiles" | "lighthouse_portfolio_metrics_workItemAgePercentiles" | "lighthouse_team_metrics_workItemAge" | "lighthouse_team_metrics_totalWorkItemAge" | "lighthouse_portfolio_metrics_throughput" | "lighthouse_portfolio_metrics_workItemAge" | "lighthouse_portfolio_metrics_totalWorkItemAge" | "lighthouse_team_metrics_blockedCountHistory" | "lighthouse_portfolio_metrics_blockedCountHistory" | "lighthouse_team_metrics_wip" | "lighthouse_team_metrics_sleRisk" | "lighthouse_team_metrics_processBehaviorChart" | "lighthouse_portfolio_metrics_processBehaviorChart" | "lighthouse_team_metrics_percentilesOverTime" | "lighthouse_portfolio_metrics_percentilesOverTime" | "lighthouse_team_metrics_processBehaviorOverTime" | "lighthouse_portfolio_metrics_processBehaviorOverTime" | "lighthouse_team_metrics_cumulativeStateTime" | "lighthouse_team_metrics_cumulativeStateTimeItems" | "lighthouse_team_metrics_cumulativeStateTimeCandidates" | "lighthouse_portfolio_metrics_cumulativeStateTime" | "lighthouse_portfolio_metrics_cumulativeStateTimeItems" | "lighthouse_portfolio_metrics_cumulativeStateTimeCandidates" | "lighthouse_feature_get" | "lighthouse_feature_workitems" | "lighthouse_delivery_list" | "lighthouse_delivery_metrics" | "lighthouse_blackout_list" | "lighthouse_blackout_create" | "lighthouse_blackout_update" | "lighthouse_blackout_delete" | "lighthouse_forecast_manual" | "lighthouse_forecast_backtest" | "lighthouse_team_report_list" | "lighthouse_team_report_get" | "lighthouse_portfolio_report_list" | "lighthouse_portfolio_report_get";
|
|
87
90
|
readonly description: string;
|
|
88
91
|
readonly inputSchema: {
|
|
89
92
|
readonly type: "object";
|
|
@@ -669,7 +672,7 @@ type McpRuntimeClient = {
|
|
|
669
672
|
readonly reason: string;
|
|
670
673
|
};
|
|
671
674
|
}>;
|
|
672
|
-
} & Omit<RefinementToolClient, "getTeam">;
|
|
675
|
+
} & Omit<RefinementToolClient, "getTeam"> & Omit<ReportToolClient, "getTeam" | "getPortfolio">;
|
|
673
676
|
export type McpCoreRuntimeDependencies = {
|
|
674
677
|
readonly createClient: () => McpRuntimeClient;
|
|
675
678
|
/** Where usage data goes; without it a tool call does exactly what it always did. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeliveryMetricsHistory, LighthouseApiResult, LighthouseClient, LighthouseVoterKeyStore, PortfolioDeliveries, RefinementMomentFacts, RefinementWordingSource, TeamRefinement, UsageDataOccurrence, UsageDataQuestionAnswer, UsageDataStep, VotedRow } from "@letpeoplework/lighthouse-client";
|
|
1
|
+
import { DeliveryMetricsHistory, LighthouseApiResult, LighthouseClient, LighthouseVoterKeyStore, PortfolioDeliveries, RefinementMomentFacts, RefinementWordingSource, ReportOwnerSource, ReportReads, TeamRefinement, UsageDataOccurrence, UsageDataQuestionAnswer, UsageDataStep, VotedRow } from "@letpeoplework/lighthouse-client";
|
|
2
2
|
import { ElicitRequestFormParams, ElicitResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -75,6 +75,9 @@ type ToolError = {
|
|
|
75
75
|
};
|
|
76
76
|
type RefinementToolClient = RefinementWordingSource<ToolError> & Pick<LighthouseClient, "getTeamRefinement" | "castRefinementVote" | "addRefinementComment" | "takeBackRefinementVote" | "getRefinementLog" | "getTerminology">;
|
|
77
77
|
//#endregion
|
|
78
|
+
//#region src/reportTools.d.ts
|
|
79
|
+
type ReportToolClient = ReportReads & ReportOwnerSource;
|
|
80
|
+
//#endregion
|
|
78
81
|
//#region src/index.d.ts
|
|
79
82
|
export type McpCorePackageContract = {
|
|
80
83
|
readonly name: "@letpeoplework/lighthouse-mcp-core";
|
|
@@ -83,7 +86,7 @@ export type McpCorePackageContract = {
|
|
|
83
86
|
};
|
|
84
87
|
export declare const getMcpCorePackageContract: () => McpCorePackageContract;
|
|
85
88
|
export type McpToolDefinition = {
|
|
86
|
-
readonly name: "lighthouse_health_check" | "lighthouse_version_get" | "lighthouse_worktracking_list" | "lighthouse_worktracking_get" | "lighthouse_team_list" | "lighthouse_team_get" | "lighthouse_team_refresh" | "lighthouse_team_refinement_get" | "lighthouse_team_refinement_vote" | "lighthouse_team_refinement_comment" | "lighthouse_team_refinement_voteTakeBack" | "lighthouse_portfolio_list" | "lighthouse_portfolio_get" | "lighthouse_portfolio_refresh" | "lighthouse_team_metrics_throughput" | "lighthouse_team_metrics_cycleTimePercentiles" | "lighthouse_team_metrics_workItemAgePercentiles" | "lighthouse_portfolio_metrics_workItemAgePercentiles" | "lighthouse_team_metrics_workItemAge" | "lighthouse_team_metrics_totalWorkItemAge" | "lighthouse_portfolio_metrics_throughput" | "lighthouse_portfolio_metrics_workItemAge" | "lighthouse_portfolio_metrics_totalWorkItemAge" | "lighthouse_team_metrics_blockedCountHistory" | "lighthouse_portfolio_metrics_blockedCountHistory" | "lighthouse_team_metrics_wip" | "lighthouse_team_metrics_sleRisk" | "lighthouse_team_metrics_processBehaviorChart" | "lighthouse_portfolio_metrics_processBehaviorChart" | "lighthouse_team_metrics_percentilesOverTime" | "lighthouse_portfolio_metrics_percentilesOverTime" | "lighthouse_team_metrics_processBehaviorOverTime" | "lighthouse_portfolio_metrics_processBehaviorOverTime" | "lighthouse_team_metrics_cumulativeStateTime" | "lighthouse_team_metrics_cumulativeStateTimeItems" | "lighthouse_team_metrics_cumulativeStateTimeCandidates" | "lighthouse_portfolio_metrics_cumulativeStateTime" | "lighthouse_portfolio_metrics_cumulativeStateTimeItems" | "lighthouse_portfolio_metrics_cumulativeStateTimeCandidates" | "lighthouse_feature_get" | "lighthouse_feature_workitems" | "lighthouse_delivery_list" | "lighthouse_delivery_metrics" | "lighthouse_blackout_list" | "lighthouse_blackout_create" | "lighthouse_blackout_update" | "lighthouse_blackout_delete" | "lighthouse_forecast_manual" | "lighthouse_forecast_backtest";
|
|
89
|
+
readonly name: "lighthouse_health_check" | "lighthouse_version_get" | "lighthouse_worktracking_list" | "lighthouse_worktracking_get" | "lighthouse_team_list" | "lighthouse_team_get" | "lighthouse_team_refresh" | "lighthouse_team_refinement_get" | "lighthouse_team_refinement_vote" | "lighthouse_team_refinement_comment" | "lighthouse_team_refinement_voteTakeBack" | "lighthouse_portfolio_list" | "lighthouse_portfolio_get" | "lighthouse_portfolio_refresh" | "lighthouse_team_metrics_throughput" | "lighthouse_team_metrics_cycleTimePercentiles" | "lighthouse_team_metrics_workItemAgePercentiles" | "lighthouse_portfolio_metrics_workItemAgePercentiles" | "lighthouse_team_metrics_workItemAge" | "lighthouse_team_metrics_totalWorkItemAge" | "lighthouse_portfolio_metrics_throughput" | "lighthouse_portfolio_metrics_workItemAge" | "lighthouse_portfolio_metrics_totalWorkItemAge" | "lighthouse_team_metrics_blockedCountHistory" | "lighthouse_portfolio_metrics_blockedCountHistory" | "lighthouse_team_metrics_wip" | "lighthouse_team_metrics_sleRisk" | "lighthouse_team_metrics_processBehaviorChart" | "lighthouse_portfolio_metrics_processBehaviorChart" | "lighthouse_team_metrics_percentilesOverTime" | "lighthouse_portfolio_metrics_percentilesOverTime" | "lighthouse_team_metrics_processBehaviorOverTime" | "lighthouse_portfolio_metrics_processBehaviorOverTime" | "lighthouse_team_metrics_cumulativeStateTime" | "lighthouse_team_metrics_cumulativeStateTimeItems" | "lighthouse_team_metrics_cumulativeStateTimeCandidates" | "lighthouse_portfolio_metrics_cumulativeStateTime" | "lighthouse_portfolio_metrics_cumulativeStateTimeItems" | "lighthouse_portfolio_metrics_cumulativeStateTimeCandidates" | "lighthouse_feature_get" | "lighthouse_feature_workitems" | "lighthouse_delivery_list" | "lighthouse_delivery_metrics" | "lighthouse_blackout_list" | "lighthouse_blackout_create" | "lighthouse_blackout_update" | "lighthouse_blackout_delete" | "lighthouse_forecast_manual" | "lighthouse_forecast_backtest" | "lighthouse_team_report_list" | "lighthouse_team_report_get" | "lighthouse_portfolio_report_list" | "lighthouse_portfolio_report_get";
|
|
87
90
|
readonly description: string;
|
|
88
91
|
readonly inputSchema: {
|
|
89
92
|
readonly type: "object";
|
|
@@ -669,7 +672,7 @@ type McpRuntimeClient = {
|
|
|
669
672
|
readonly reason: string;
|
|
670
673
|
};
|
|
671
674
|
}>;
|
|
672
|
-
} & Omit<RefinementToolClient, "getTeam">;
|
|
675
|
+
} & Omit<RefinementToolClient, "getTeam"> & Omit<ReportToolClient, "getTeam" | "getPortfolio">;
|
|
673
676
|
export type McpCoreRuntimeDependencies = {
|
|
674
677
|
readonly createClient: () => McpRuntimeClient;
|
|
675
678
|
/** Where usage data goes; without it a tool call does exactly what it always did. */
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LIGHTHOUSE_IS_REACHABLE, REFINEMENT_ANSWERS, describeAsOfHeading, describeBacktestActual, describeBacktestPeriod, describeBacktestSummary, describeBlackoutRuleCount, describeBlackoutRuleWriteConfirmation, describeBlockedDays, describeBlockedNow, describeConnectionSummary, describeCycleTimeDays, describeDeliveryCount, describeDeliveryMetricsHeading, describeFeatureListCount, describeFeatureWorkItemsHeading, describeInProgressNow, describeManualForecastLikelihood, describeManualForecastSummary, describeMetricSummary, describeMetricsHeading, describeMissingCondition, describeNothingToTakeBack, describeOwnerCount, describePercentilesOverTimeDays, describePortfolioSummary, describeProcessBehaviorChart, describeProcessBehaviorOverTimeDays, describeRecordedComment, describeRecordedVote, describeRefinementSummary, describeRefreshConfirmation, describeSleRiskNow, describeTakenBack, describeTeamSummary, describeThroughputDays, describeTimeInStateContributorDays, describeTimeInStateDays, describeTotalWorkItemAgeDays, describeVersion, describeWhatWipLeavesUnsaid, describeWorkItemAgeDays, describeWorkItemAgePercentiles, describeWorkTrackingSystemCount, getDefaultMetricsDateRange, isMissingItsCondition, isRefinementAnswer, keepVoterKey, myVoteOn, readAnswerWording, readBacktest, readBlackoutRules, readBlocked, readCycleTimeDefinitionName, readCycleTimePercentiles, readDeliveryMetricsHistory, readFeatureList, readFeatureWording, readFeatureWorkItems, readInProgressItems, readManualForecast, readNameOfMyVote, readOwnerList, readPercentilesOverTime, readPortfolio, readPortfolioDeliveries, readProcessBehaviorChart, readProcessBehaviorOverTime, readRefinementWording, readRunChart, readServiceLevelExpectation, readSleRisk, readSystemWipLimit, readTeam, readTerms, readTimeInStateBar, readTimeInStateContributors, readTotalWorkItemAge, readVoteRefusal, readWorkItemAge, readWorkItemAgePercentiles, readWorkTrackingConnections, readWrittenBlackoutRule, refinementDayVerdictOf, sizingMomentOf, summariseDeliveryMetricsHistory } from "@letpeoplework/lighthouse-client";
|
|
1
|
+
import { LIGHTHOUSE_IS_REACHABLE, REFINEMENT_ANSWERS, describeAsOfHeading, describeBacktestActual, describeBacktestPeriod, describeBacktestSummary, describeBlackoutRuleCount, describeBlackoutRuleWriteConfirmation, describeBlockedDays, describeBlockedNow, describeConnectionSummary, describeCycleTimeDays, describeDeliveryCount, describeDeliveryMetricsHeading, describeFeatureListCount, describeFeatureWorkItemsHeading, describeInProgressNow, describeManualForecastLikelihood, describeManualForecastSummary, describeMetricSummary, describeMetricsHeading, describeMissingCondition, describeNothingToTakeBack, describeOwnerCount, describePercentilesOverTimeDays, describePortfolioSummary, describeProcessBehaviorChart, describeProcessBehaviorOverTimeDays, describeRecordedComment, describeRecordedVote, describeRefinementSummary, describeRefreshConfirmation, describeSleRiskNow, describeStaleNow, describeTakenBack, describeTeamSummary, describeThroughputDays, describeTimeInStateContributorDays, describeTimeInStateDays, describeTotalWorkItemAgeDays, describeVersion, describeWhatWipLeavesUnsaid, describeWorkItemAgeDays, describeWorkItemAgePercentiles, describeWorkTrackingSystemCount, getDefaultMetricsDateRange, isMissingItsCondition, isRefinementAnswer, keepVoterKey, myVoteOn, readAnswerWording, readBacktest, readBlackoutRules, readBlocked, readCycleTimeDefinitionName, readCycleTimePercentiles, readDeliveryMetricsHistory, readFeatureList, readFeatureWording, readFeatureWorkItems, readInProgressItems, readManualForecast, readNameOfMyVote, readOwnerList, readOwnersReport, readOwnersReports, readPercentilesOverTime, readPortfolio, readPortfolioDeliveries, readProcessBehaviorChart, readProcessBehaviorOverTime, readRefinementWording, readReportOwner, readReportRefusal, readRunChart, readServiceLevelExpectation, readSleRisk, readSystemWipLimit, readTeam, readTerms, readTimeInStateBar, readTimeInStateContributors, readTotalWorkItemAge, readVoteRefusal, readWorkItemAge, readWorkItemAgePercentiles, readWorkTrackingConnections, readWrittenBlackoutRule, refinementDayVerdictOf, reportOpenedOccurrencesOf, sizingMomentOf, summariseDeliveryMetricsHistory, summariseReport, summariseReportList } from "@letpeoplework/lighthouse-client";
|
|
2
2
|
import { ElicitResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { encode } from "@toon-format/toon";
|
|
@@ -425,6 +425,119 @@ const REFINEMENT_TOOLS = /* @__PURE__ */ new Map([
|
|
|
425
425
|
/** The handler of a refinement tool, or undefined for a tool that is not one. */
|
|
426
426
|
const findRefinementTool = (name) => REFINEMENT_TOOLS.get(name);
|
|
427
427
|
//#endregion
|
|
428
|
+
//#region src/reportTools.ts
|
|
429
|
+
const HOW_LIGHTHOUSE_SHOWS_IT = "Read the lighthouse-reports skill for how Lighthouse shows these values, and quote them as they come.";
|
|
430
|
+
const HOW_THE_ANSWER_READS = "The first block is Lighthouse's answer, unchanged; a second text block, `summary`,";
|
|
431
|
+
const LIST_LABEL = "reports";
|
|
432
|
+
const GET_LABEL = "report";
|
|
433
|
+
const NEEDS_A_SERVER = "Needs a Lighthouse newer than v26.10.10.1.";
|
|
434
|
+
const ownerIdProperty = (kind) => ({
|
|
435
|
+
type: "integer",
|
|
436
|
+
description: `The ${kind}'s numeric id.`
|
|
437
|
+
});
|
|
438
|
+
const reportIdProperty = {
|
|
439
|
+
type: "integer",
|
|
440
|
+
description: "The report's id, as its owner's report list names it under reports[].id."
|
|
441
|
+
};
|
|
442
|
+
const listInputSchema = (kind) => ({
|
|
443
|
+
type: "object",
|
|
444
|
+
properties: { id: ownerIdProperty(kind) },
|
|
445
|
+
required: ["id"],
|
|
446
|
+
additionalProperties: false
|
|
447
|
+
});
|
|
448
|
+
const getInputSchema = (kind) => ({
|
|
449
|
+
type: "object",
|
|
450
|
+
properties: {
|
|
451
|
+
id: ownerIdProperty(kind),
|
|
452
|
+
reportId: reportIdProperty
|
|
453
|
+
},
|
|
454
|
+
required: ["id", "reportId"],
|
|
455
|
+
additionalProperties: false
|
|
456
|
+
});
|
|
457
|
+
const listDescription = (kind) => `List the reports a ${kind} holds by ${kind} ID: each report's id, name, type (templateKey) and when it was created. ${HOW_THE_ANSWER_READS} names the ${kind} and counts its reports in the instance's terminology. ${HOW_LIGHTHOUSE_SHOWS_IT} ${NEEDS_A_SERVER}`;
|
|
458
|
+
const getDescription = (kind) => `Get one of a ${kind}'s reports by ${kind} ID and reportId, exactly as Lighthouse answers it: its Then side as Lighthouse froze it, its Now side read afresh up to today, and every value, a missing value with its reason. ${HOW_LIGHTHOUSE_SHOWS_IT} ${HOW_THE_ANSWER_READS} names the report, its ${kind} and its Then and Now windows. ${NEEDS_A_SERVER}`;
|
|
459
|
+
const reportToolDefinitions = [
|
|
460
|
+
{
|
|
461
|
+
name: "lighthouse_team_report_list",
|
|
462
|
+
description: listDescription("team"),
|
|
463
|
+
inputSchema: listInputSchema("team")
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
name: "lighthouse_team_report_get",
|
|
467
|
+
description: getDescription("team"),
|
|
468
|
+
inputSchema: getInputSchema("team")
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
name: "lighthouse_portfolio_report_list",
|
|
472
|
+
description: listDescription("portfolio"),
|
|
473
|
+
inputSchema: listInputSchema("portfolio")
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: "lighthouse_portfolio_report_get",
|
|
477
|
+
description: getDescription("portfolio"),
|
|
478
|
+
inputSchema: getInputSchema("portfolio")
|
|
479
|
+
}
|
|
480
|
+
];
|
|
481
|
+
const listSchema = z.object({ id: z.number().int() });
|
|
482
|
+
const getSchema = z.object({
|
|
483
|
+
id: z.number().int(),
|
|
484
|
+
reportId: z.number().int()
|
|
485
|
+
});
|
|
486
|
+
const reportInputSchemas = {
|
|
487
|
+
lighthouse_team_report_list: listSchema,
|
|
488
|
+
lighthouse_team_report_get: getSchema,
|
|
489
|
+
lighthouse_portfolio_report_list: listSchema,
|
|
490
|
+
lighthouse_portfolio_report_get: getSchema
|
|
491
|
+
};
|
|
492
|
+
const getReportId = (argumentsPayload) => {
|
|
493
|
+
const reportId = typeof argumentsPayload === "object" && argumentsPayload !== null ? argumentsPayload.reportId : void 0;
|
|
494
|
+
return typeof reportId === "number" && Number.isInteger(reportId) ? reportId : null;
|
|
495
|
+
};
|
|
496
|
+
const answerReportRead = async (client, owner, result, answer) => {
|
|
497
|
+
if (!result.ok) return getErrorToolResult(`${answer.label}: ${await readReportRefusal(client, result.error, owner.kind, answer.read)}`);
|
|
498
|
+
const ownerName = await readReportOwner(client, owner);
|
|
499
|
+
return withSummaryBlock(`${answer.label}: ${encodePayload(result.value)}`, answer.summarise(result.value, ownerName));
|
|
500
|
+
};
|
|
501
|
+
const invalid = (label, what) => getErrorToolResult(`${label}: invalid ${what}`);
|
|
502
|
+
const reportList = (kind) => async (argumentsPayload, client) => {
|
|
503
|
+
const id = getNumericId(argumentsPayload);
|
|
504
|
+
if (id === null) return invalid(LIST_LABEL, "id");
|
|
505
|
+
const owner = {
|
|
506
|
+
kind,
|
|
507
|
+
id
|
|
508
|
+
};
|
|
509
|
+
return answerReportRead(client, owner, await readOwnersReports(client, owner), {
|
|
510
|
+
label: LIST_LABEL,
|
|
511
|
+
read: "list",
|
|
512
|
+
summarise: summariseReportList
|
|
513
|
+
});
|
|
514
|
+
};
|
|
515
|
+
const reportGet = (kind) => async (argumentsPayload, client) => {
|
|
516
|
+
const id = getNumericId(argumentsPayload);
|
|
517
|
+
if (id === null) return invalid(GET_LABEL, "id");
|
|
518
|
+
const reportId = getReportId(argumentsPayload);
|
|
519
|
+
if (reportId === null) return invalid(GET_LABEL, "reportId");
|
|
520
|
+
const owner = {
|
|
521
|
+
kind,
|
|
522
|
+
id
|
|
523
|
+
};
|
|
524
|
+
const result = await readOwnersReport(client, owner, reportId);
|
|
525
|
+
const answered = await answerReportRead(client, owner, result, {
|
|
526
|
+
label: GET_LABEL,
|
|
527
|
+
read: "report",
|
|
528
|
+
summarise: summariseReport
|
|
529
|
+
});
|
|
530
|
+
return countedToolResult(answered, reportOpenedOccurrencesOf(result));
|
|
531
|
+
};
|
|
532
|
+
const REPORT_TOOLS = /* @__PURE__ */ new Map([
|
|
533
|
+
["lighthouse_team_report_list", reportList("team")],
|
|
534
|
+
["lighthouse_team_report_get", reportGet("team")],
|
|
535
|
+
["lighthouse_portfolio_report_list", reportList("portfolio")],
|
|
536
|
+
["lighthouse_portfolio_report_get", reportGet("portfolio")]
|
|
537
|
+
]);
|
|
538
|
+
/** The handler of one of the report tools, or undefined for any other tool. */
|
|
539
|
+
const findReportTool = (name) => REPORT_TOOLS.get(name);
|
|
540
|
+
//#endregion
|
|
428
541
|
//#region src/index.ts
|
|
429
542
|
const getMcpCorePackageContract = () => ({
|
|
430
543
|
name: "@letpeoplework/lighthouse-mcp-core",
|
|
@@ -560,6 +673,7 @@ const recurringBlackoutRuleProperties = {
|
|
|
560
673
|
description: "Human-readable description of the rule."
|
|
561
674
|
}
|
|
562
675
|
};
|
|
676
|
+
const BEYOND_HORIZON_NOTE = "A forecast percentile with beyondHorizon true lies beyond Lighthouse's simulation horizon of horizonYears years: its expectedDate is only a stand-in, not a forecast, so say \"More than <horizonYears> years\" and never that date. Both fields are absent on an older Lighthouse, whose dates stand as sent.";
|
|
563
677
|
const toolDefinitions = [
|
|
564
678
|
{
|
|
565
679
|
name: "lighthouse_health_check",
|
|
@@ -689,7 +803,7 @@ const toolDefinitions = [
|
|
|
689
803
|
},
|
|
690
804
|
{
|
|
691
805
|
name: "lighthouse_portfolio_metrics_blockedCountHistory",
|
|
692
|
-
description: "Get the blocked-items-over-time trend for a portfolio by ID: how many work items were blocked on each captured day, optionally filtered by start and end dates. No tool reads what is blocked right now for a portfolio: name the command `lh metrics portfolio --metrics wip --id <id>`, whose items each carry isBlocked and, when blocked, a blockedSince timestamp. A second text block, `summary: …`, states the answer as the dashboard does, in the instance's terminology: the heading and its sentence; the first block is the facts, unchanged.",
|
|
806
|
+
description: "Get the blocked-items-over-time trend for a portfolio by ID: how many work items were blocked on each captured day, optionally filtered by start and end dates. No tool reads what is blocked right now for a portfolio: name the command `lh metrics portfolio --metrics wip --id <id>`, whose items each carry isBlocked and, when blocked, a blockedSince timestamp, and whether Lighthouse marks it stale (isStale) and why (staleReasons), both absent on an older Lighthouse. A second text block, `summary: …`, states the answer as the dashboard does, in the instance's terminology: the heading and its sentence; the first block is the facts, unchanged.",
|
|
693
807
|
inputSchema: {
|
|
694
808
|
type: "object",
|
|
695
809
|
properties: {
|
|
@@ -823,7 +937,7 @@ const toolDefinitions = [
|
|
|
823
937
|
},
|
|
824
938
|
{
|
|
825
939
|
name: "lighthouse_feature_get",
|
|
826
|
-
description: "Get feature details by numeric IDs or external reference IDs. A second text block, `summary`, counts them in the instance's terminology; the first block is the facts, unchanged.",
|
|
940
|
+
description: "Get feature details by numeric IDs or external reference IDs. A second text block, `summary`, counts them in the instance's terminology; the first block is the facts, unchanged. " + BEYOND_HORIZON_NOTE,
|
|
827
941
|
inputSchema: {
|
|
828
942
|
type: "object",
|
|
829
943
|
properties: {
|
|
@@ -848,12 +962,12 @@ const toolDefinitions = [
|
|
|
848
962
|
},
|
|
849
963
|
{
|
|
850
964
|
name: "lighthouse_delivery_list",
|
|
851
|
-
description: "List deliveries for a portfolio by portfolio ID. The answer holds the running deliveries under `active` and the archived ones under `archived`; a `summary` field counts the running ones in the instance's terminology.",
|
|
965
|
+
description: "List deliveries for a portfolio by portfolio ID. The answer holds the running deliveries under `active` and the archived ones under `archived`; a `summary` field counts the running ones in the instance's terminology. " + BEYOND_HORIZON_NOTE,
|
|
852
966
|
inputSchema: idInputSchema
|
|
853
967
|
},
|
|
854
968
|
{
|
|
855
969
|
name: "lighthouse_delivery_metrics",
|
|
856
|
-
description: "Get a delivery's recorded trend by delivery ID: one row per day with total, done and remaining work, the epic count and the likelihood. Set detail to \"epics\" for the per-epic breakdown and the forecast distribution, which are far larger. Forward-only, so it starts at the first recorded snapshot. `summary` states the delivery and its date as lh heads them, in the instance's terminology: a second text block beside the rows, a field of the detailed payload. Requires Lighthouse newer than v26.5.29.5.",
|
|
970
|
+
description: "Get a delivery's recorded trend by delivery ID: one row per day with total, done and remaining work, the epic count and the likelihood. Set detail to \"epics\" for the per-epic breakdown and the forecast distribution, which are far larger. Forward-only, so it starts at the first recorded snapshot. `summary` states the delivery and its date as lh heads them, in the instance's terminology: a second text block beside the rows, a field of the detailed payload. Requires Lighthouse newer than v26.5.29.5. " + BEYOND_HORIZON_NOTE,
|
|
857
971
|
inputSchema: {
|
|
858
972
|
type: "object",
|
|
859
973
|
properties: {
|
|
@@ -917,7 +1031,7 @@ const toolDefinitions = [
|
|
|
917
1031
|
},
|
|
918
1032
|
{
|
|
919
1033
|
name: "lighthouse_forecast_manual",
|
|
920
|
-
description: "Run a manual forecast for a team by ID with optional remaining items and target date. Pass applyFilterOverride=true to apply the team's forecast filter, false to skip it, or omit to respect the team setting (Lighthouse v26.5.24.10+). The response includes filterApplied (boolean) and excludedSummary (string) when a filter was applied. `summary` states the answer as the web does, in the instance's terminology: the heading, then the likelihood sentence when both remaining items and a target date were given.",
|
|
1034
|
+
description: "Run a manual forecast for a team by ID with optional remaining items and target date. Pass applyFilterOverride=true to apply the team's forecast filter, false to skip it, or omit to respect the team setting (Lighthouse v26.5.24.10+). The response includes filterApplied (boolean) and excludedSummary (string) when a filter was applied. `summary` states the answer as the web does, in the instance's terminology: the heading, then the likelihood sentence when both remaining items and a target date were given. " + BEYOND_HORIZON_NOTE,
|
|
921
1035
|
inputSchema: {
|
|
922
1036
|
type: "object",
|
|
923
1037
|
properties: {
|
|
@@ -1057,7 +1171,7 @@ const toolDefinitions = [
|
|
|
1057
1171
|
},
|
|
1058
1172
|
{
|
|
1059
1173
|
name: "lighthouse_team_metrics_wip",
|
|
1060
|
-
description: "Get what a team has in progress right now by ID: each work item in progress today with its age (workItemAge), state, whether it is blocked (isBlocked) and since when (blockedSince), and its link (url). Use it for what is blocked or aging today; for how many were blocked on past days use lighthouse_team_metrics_blockedCountHistory. A second text block, `summary: …`, states the answer as lh does, in the instance's terminology: the heading, how many are in progress against the System WIP Limit, and how many are
|
|
1174
|
+
description: "Get what a team has in progress right now by ID: each work item in progress today with its age (workItemAge), state, whether it is blocked (isBlocked) and since when (blockedSince), whether Lighthouse marks it stale (isStale) and why (staleReasons: each a kind, TimeInState, BlockedDuration or TimeInStateWhileBlocked, with its days, and the state's stateName on the two time-in-state kinds), and its link (url). isStale and staleReasons are absent on an older Lighthouse, which does not say which work items are stale; never work staleness out yourself. Use it for what is blocked, stale or aging today; for how many were blocked on past days use lighthouse_team_metrics_blockedCountHistory. A second text block, `summary: …`, states the answer as lh does, in the instance's terminology: the heading, how many are in progress against the System WIP Limit, how many are blocked and how many are stale; the first block is the facts, unchanged.",
|
|
1061
1175
|
inputSchema: idInputSchema
|
|
1062
1176
|
},
|
|
1063
1177
|
{
|
|
@@ -1074,7 +1188,8 @@ const toolDefinitions = [
|
|
|
1074
1188
|
name: "lighthouse_portfolio_metrics_processBehaviorChart",
|
|
1075
1189
|
description: "Get one Process Behaviour Chart of a portfolio by ID, as Lighthouse computes it: its status (and statusReason when it is not Ready), the average and natural process limits, whether a baseline is configured, and every point (xValue, yValue) with the signals Lighthouse found on it (specialCauses: LargeChange, ModerateChange, ModerateShift, SmallShift; None means no signal) and whether it falls on a blackout day (isBlackout). metricType picks the chart: Throughput, Arrivals, Wip, WorkItemAge (the Total Work Item Age chart), CycleTime or FeatureSize. A point is a day, except on CycleTime, which has one point per finished work item, and FeatureSize, which has one per finished feature, each dated when it finished, so several can share a day. Use it for \"is something unusual going on\" and \"did our process change\"; never work a signal out yourself. Optionally filtered by start and end dates. A second text block, `summary: …`, states the answer as lh does, in the instance's terminology: the heading, the chart's title, and each signal with the days it fired, or that there are no signals; the facts are unchanged.",
|
|
1076
1190
|
inputSchema: chartInputSchema(processBehaviorMetricTypes)
|
|
1077
|
-
}
|
|
1191
|
+
},
|
|
1192
|
+
...reportToolDefinitions
|
|
1078
1193
|
];
|
|
1079
1194
|
const getDefinitionId = (argumentsPayload) => {
|
|
1080
1195
|
if (typeof argumentsPayload !== "object" || argumentsPayload === null || Array.isArray(argumentsPayload)) return;
|
|
@@ -1196,7 +1311,8 @@ const summariseCurrentWip = (today) => (facts, known) => {
|
|
|
1196
1311
|
if (now === null) return null;
|
|
1197
1312
|
const { terms } = known.wording;
|
|
1198
1313
|
const blocked = describeBlockedNow(now, "team", terms);
|
|
1199
|
-
|
|
1314
|
+
const stale = describeStaleNow(now, "team", terms);
|
|
1315
|
+
return linesOf(describeAsOfHeading({ endDate: today }, known.wording), sentenceOf(describeInProgressNow(now, "team", terms, known.systemWipLimit ?? void 0)), blocked === null ? null : sentenceOf(blocked), stale === null ? null : sentenceOf(stale), ...describeWhatWipLeavesUnsaid(now, "team", terms, known.systemWipLimit));
|
|
1200
1316
|
};
|
|
1201
1317
|
const readTeamForSummary = async (client, teamId) => {
|
|
1202
1318
|
const team = client.getTeam(teamId);
|
|
@@ -1360,6 +1476,7 @@ const toolInputSchemas = {
|
|
|
1360
1476
|
lighthouse_team_refresh: z.object({ id: z.number().int() }),
|
|
1361
1477
|
lighthouse_team_refinement_get: z.object({ id: z.number().int() }),
|
|
1362
1478
|
...refinementWriteInputSchemas,
|
|
1479
|
+
...reportInputSchemas,
|
|
1363
1480
|
lighthouse_portfolio_list: z.object({}),
|
|
1364
1481
|
lighthouse_portfolio_get: z.object({ id: z.number().int() }),
|
|
1365
1482
|
lighthouse_portfolio_refresh: z.object({ id: z.number().int() }),
|
|
@@ -1586,6 +1703,8 @@ const answerToolCall = (dependencies) => async (name, argumentsPayload) => {
|
|
|
1586
1703
|
}
|
|
1587
1704
|
const refinementTool = findRefinementTool(name);
|
|
1588
1705
|
if (refinementTool !== void 0) return refinementTool(argumentsPayload, client, dependencies);
|
|
1706
|
+
const reportTool = findReportTool(name);
|
|
1707
|
+
if (reportTool !== void 0) return reportTool(argumentsPayload, client);
|
|
1589
1708
|
if (name === "lighthouse_portfolio_list") return answerOwnerList("portfolio", "portfolios", client.listPortfolios(), client);
|
|
1590
1709
|
if (name === "lighthouse_portfolio_get") {
|
|
1591
1710
|
const id = getNumericId(argumentsPayload);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@letpeoplework/lighthouse-mcp-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Shared MCP semantics for Lighthouse",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"registry": "https://registry.npmjs.org/"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@letpeoplework/lighthouse-client": "1.
|
|
29
|
+
"@letpeoplework/lighthouse-client": "1.12.0",
|
|
30
30
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
31
31
|
"@toon-format/toon": "^4.0.0",
|
|
32
32
|
"zod": "^4.0.0"
|