@productbrain/mcp 0.0.1-beta.25 → 0.0.1-beta.27
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/{chunk-E75NDP6K.js → chunk-R4LVIGFQ.js} +179 -13
- package/dist/chunk-R4LVIGFQ.js.map +1 -0
- package/dist/{chunk-EVF3CYZN.js → chunk-YCHKN75N.js} +112 -21
- package/dist/chunk-YCHKN75N.js.map +1 -0
- package/dist/http.js +2 -2
- package/dist/index.js +2 -2
- package/dist/{smart-capture-NX4QDLLY.js → smart-capture-WWGVXMQ2.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-E75NDP6K.js.map +0 -1
- package/dist/chunk-EVF3CYZN.js.map +0 -1
- /package/dist/{smart-capture-NX4QDLLY.js.map → smart-capture-WWGVXMQ2.js.map} +0 -0
|
@@ -10,10 +10,11 @@ import {
|
|
|
10
10
|
mcpQuery,
|
|
11
11
|
recordSessionActivity,
|
|
12
12
|
registerSmartCaptureTools,
|
|
13
|
+
requireActiveSession,
|
|
13
14
|
requireWriteAccess,
|
|
14
15
|
setSessionOriented,
|
|
15
16
|
startAgentSession
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-YCHKN75N.js";
|
|
17
18
|
|
|
18
19
|
// src/server.ts
|
|
19
20
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -429,6 +430,9 @@ The relation was **not applied** \u2014 it will be created when the proposal is
|
|
|
429
430
|
const created = results.filter((r) => r.ok && !r.proposalCreated);
|
|
430
431
|
const proposals = results.filter((r) => r.ok && r.proposalCreated);
|
|
431
432
|
const failed = results.filter((r) => !r.ok);
|
|
433
|
+
for (let i = 0; i < created.length; i++) {
|
|
434
|
+
await recordSessionActivity({ relationCreated: true });
|
|
435
|
+
}
|
|
432
436
|
const lines = [`# Batch Link Results
|
|
433
437
|
`];
|
|
434
438
|
lines.push(`**${created.length}** created, **${proposals.length}** proposals, **${failed.length}** failed out of ${relations.length} total.
|
|
@@ -690,6 +694,82 @@ Use \`suggest-links\` to discover potential connections, or \`relate-entries\` t
|
|
|
690
694
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
691
695
|
}
|
|
692
696
|
);
|
|
697
|
+
server.registerTool(
|
|
698
|
+
"get-build-context",
|
|
699
|
+
{
|
|
700
|
+
title: "Get Build Context",
|
|
701
|
+
description: "Assemble a structured build spec for any entry. Collection-agnostic: works with bets, features, epics, or any collection type. Returns the entry data, related entries by collection, applicable business rules, glossary terms, and chain refs to consult. Use this when starting a build to get full context in one call \u2014 no need to call orient, gather-context, and search separately.\n\nPass an entry ID (e.g. 'ENT-wc7u71', 'BET-001') or name. The output adapts to the entry's collection schema.",
|
|
702
|
+
inputSchema: {
|
|
703
|
+
entryId: z.string().describe("Entry ID or name (e.g. 'ENT-wc7u71', 'PB as Shared Memory')"),
|
|
704
|
+
maxHops: z.number().min(1).max(3).default(2).optional().describe("Relation traversal depth (default 2)")
|
|
705
|
+
},
|
|
706
|
+
annotations: { readOnlyHint: true }
|
|
707
|
+
},
|
|
708
|
+
async ({ entryId, maxHops }) => {
|
|
709
|
+
requireActiveSession();
|
|
710
|
+
const result = await mcpQuery("chain.assembleBuildContext", {
|
|
711
|
+
entryId,
|
|
712
|
+
maxHops: maxHops ?? 2
|
|
713
|
+
});
|
|
714
|
+
const { entry, relatedByCollection, businessRules, glossaryTerms, rulesToHonor, chainRefs, contextTruncated } = result;
|
|
715
|
+
const lines = [
|
|
716
|
+
`# Build Context: ${entry.name}`,
|
|
717
|
+
`**Entry:** ${entry.entryId ?? entry.name} [${entry.collectionSlug}]`,
|
|
718
|
+
`**Status:** ${entry.status}`,
|
|
719
|
+
""
|
|
720
|
+
];
|
|
721
|
+
if (Object.keys(entry.data).length > 0) {
|
|
722
|
+
lines.push("## Entry Data");
|
|
723
|
+
for (const [key, val] of Object.entries(entry.data)) {
|
|
724
|
+
const str = typeof val === "string" ? val : JSON.stringify(val);
|
|
725
|
+
if (str.length > 200) {
|
|
726
|
+
lines.push(`- **${key}:** ${str.slice(0, 200)}...`);
|
|
727
|
+
} else {
|
|
728
|
+
lines.push(`- **${key}:** ${str}`);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
lines.push("");
|
|
732
|
+
}
|
|
733
|
+
if (businessRules.length > 0) {
|
|
734
|
+
lines.push("## Business Rules to Honor");
|
|
735
|
+
for (const r of businessRules) {
|
|
736
|
+
const id = r.entryId ?? r.name;
|
|
737
|
+
lines.push(`- **${id}:** ${r.name}`);
|
|
738
|
+
if (r.description) lines.push(` ${r.description.slice(0, 150)}${r.description.length > 150 ? "..." : ""}`);
|
|
739
|
+
}
|
|
740
|
+
lines.push("");
|
|
741
|
+
}
|
|
742
|
+
if (glossaryTerms.length > 0) {
|
|
743
|
+
lines.push("## Glossary Terms");
|
|
744
|
+
for (const t of glossaryTerms.slice(0, 15)) {
|
|
745
|
+
const id = t.entryId ?? t.name;
|
|
746
|
+
lines.push(`- **${id}:** ${t.name}`);
|
|
747
|
+
if (t.definition) lines.push(` ${t.definition.slice(0, 120)}${t.definition.length > 120 ? "..." : ""}`);
|
|
748
|
+
}
|
|
749
|
+
if (glossaryTerms.length > 15) lines.push(` _...and ${glossaryTerms.length - 15} more_`);
|
|
750
|
+
lines.push("");
|
|
751
|
+
}
|
|
752
|
+
const relatedCount = Object.values(relatedByCollection).reduce((sum, arr) => sum + arr.length, 0);
|
|
753
|
+
if (relatedCount > 0) {
|
|
754
|
+
lines.push("## Related Entries");
|
|
755
|
+
for (const [coll, entries] of Object.entries(relatedByCollection)) {
|
|
756
|
+
lines.push(`### ${coll} (${entries.length})`);
|
|
757
|
+
for (const e of entries.slice(0, 5)) {
|
|
758
|
+
lines.push(`- ${e.entryId ?? e.name}: ${e.name} [${e.relationType}]`);
|
|
759
|
+
}
|
|
760
|
+
if (entries.length > 5) lines.push(` _...and ${entries.length - 5} more_`);
|
|
761
|
+
}
|
|
762
|
+
lines.push("");
|
|
763
|
+
}
|
|
764
|
+
lines.push("## Chain Refs to Consult");
|
|
765
|
+
lines.push(chainRefs.slice(0, 15).map((r) => `- ${r}`).join("\n"));
|
|
766
|
+
if (contextTruncated) {
|
|
767
|
+
lines.push("");
|
|
768
|
+
lines.push("_Context was truncated (limits: 50 related, 20 rules, 30 glossary). Use get-entry for full details._");
|
|
769
|
+
}
|
|
770
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
771
|
+
}
|
|
772
|
+
);
|
|
693
773
|
server.registerTool(
|
|
694
774
|
"suggest-links",
|
|
695
775
|
{
|
|
@@ -920,7 +1000,7 @@ Use \`list-collections\` to verify the result.`
|
|
|
920
1000
|
},
|
|
921
1001
|
async ({ entryId }) => {
|
|
922
1002
|
requireWriteAccess();
|
|
923
|
-
const { runContradictionCheck } = await import("./smart-capture-
|
|
1003
|
+
const { runContradictionCheck } = await import("./smart-capture-WWGVXMQ2.js");
|
|
924
1004
|
const entry = await mcpQuery("chain.getEntry", { entryId });
|
|
925
1005
|
if (!entry) {
|
|
926
1006
|
return {
|
|
@@ -1103,7 +1183,21 @@ async function queryPlannedWork() {
|
|
|
1103
1183
|
function hasPlannedWork(work) {
|
|
1104
1184
|
return work.uncommittedDrafts.length > 0 || work.inProgressEntries.length > 0 || work.openTensions.length > 0;
|
|
1105
1185
|
}
|
|
1106
|
-
function
|
|
1186
|
+
function formatRecoveryBlock(block) {
|
|
1187
|
+
const fmt = (items, overflow) => items.map((e) => `${e.entryId} [${e.collection}] ${e.name}`).join(", ") + (overflow && overflow > 0 ? ` (+${overflow} more)` : "");
|
|
1188
|
+
const lines = [
|
|
1189
|
+
"## Previous Session Recovery",
|
|
1190
|
+
"",
|
|
1191
|
+
`Session ${block.sessionId} \u2014 ${block.date}, ${block.duration}, status: ${block.status}`,
|
|
1192
|
+
`Created (${block.created.length + (block.createdOverflow ?? 0)}): ${fmt(block.created, block.createdOverflow)}`,
|
|
1193
|
+
`Modified (${block.modified.length + (block.modifiedOverflow ?? 0)}): ${fmt(block.modified, block.modifiedOverflow)}`,
|
|
1194
|
+
`Drafts needing attention (${block.draftsNeedingAttention.length + (block.draftsOverflow ?? 0)}): ${fmt(block.draftsNeedingAttention, block.draftsOverflow)}`,
|
|
1195
|
+
`Relations created: ${block.relationsCreated}`,
|
|
1196
|
+
""
|
|
1197
|
+
];
|
|
1198
|
+
return lines;
|
|
1199
|
+
}
|
|
1200
|
+
function buildPlannedWorkSection(work, priorSessions, recoveryBlock) {
|
|
1107
1201
|
if (!hasPlannedWork(work) && priorSessions.length === 0) return [];
|
|
1108
1202
|
const lines = [];
|
|
1109
1203
|
lines.push("## Continue from where you left off");
|
|
@@ -1133,7 +1227,7 @@ function buildPlannedWorkSection(work, priorSessions) {
|
|
|
1133
1227
|
lines.push(`- **${count} open tensions** \u2014 top: ${top.name}`);
|
|
1134
1228
|
}
|
|
1135
1229
|
}
|
|
1136
|
-
if (priorSessions.length > 0 && !hasPlannedWork(work)) {
|
|
1230
|
+
if (priorSessions.length > 0 && !hasPlannedWork(work) && !recoveryBlock) {
|
|
1137
1231
|
const last = priorSessions[0];
|
|
1138
1232
|
const date = new Date(last.startedAt).toISOString().split("T")[0];
|
|
1139
1233
|
const created = Array.isArray(last.entriesCreated) ? last.entriesCreated.length : last.entriesCreated ?? 0;
|
|
@@ -1142,6 +1236,10 @@ function buildPlannedWorkSection(work, priorSessions) {
|
|
|
1142
1236
|
lines.push(`- **Last session** (${date}): ${created} created, ${modified} modified`);
|
|
1143
1237
|
}
|
|
1144
1238
|
}
|
|
1239
|
+
if (recoveryBlock) {
|
|
1240
|
+
lines.push("");
|
|
1241
|
+
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
1242
|
+
}
|
|
1145
1243
|
lines.push("");
|
|
1146
1244
|
return lines;
|
|
1147
1245
|
}
|
|
@@ -1222,6 +1320,27 @@ function buildSessionSummary(log) {
|
|
|
1222
1320
|
}
|
|
1223
1321
|
return lines.join("\n");
|
|
1224
1322
|
}
|
|
1323
|
+
function extractSessionEntryIds(priorSessions) {
|
|
1324
|
+
const allSeen = /* @__PURE__ */ new Set();
|
|
1325
|
+
const all = [];
|
|
1326
|
+
let lastSessionOnly = [];
|
|
1327
|
+
for (let i = 0; i < priorSessions.length; i++) {
|
|
1328
|
+
const s = priorSessions[i];
|
|
1329
|
+
const ids = [...s.entriesCreated ?? [], ...s.entriesModified ?? []].filter((id) => id);
|
|
1330
|
+
if (i === 0) {
|
|
1331
|
+
lastSessionOnly = [...new Set(ids)].slice(0, 5);
|
|
1332
|
+
}
|
|
1333
|
+
for (const id of ids) {
|
|
1334
|
+
if (!allSeen.has(id)) {
|
|
1335
|
+
allSeen.add(id);
|
|
1336
|
+
all.push(id);
|
|
1337
|
+
if (all.length >= 10) break;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
if (all.length >= 10) break;
|
|
1341
|
+
}
|
|
1342
|
+
return { all, lastSessionOnly };
|
|
1343
|
+
}
|
|
1225
1344
|
function registerHealthTools(server) {
|
|
1226
1345
|
server.registerTool(
|
|
1227
1346
|
"health",
|
|
@@ -1362,15 +1481,23 @@ function registerHealthTools(server) {
|
|
|
1362
1481
|
errors.push(`Workspace: ${e.message}`);
|
|
1363
1482
|
}
|
|
1364
1483
|
let priorSessions = [];
|
|
1484
|
+
let recoveryBlock = null;
|
|
1365
1485
|
if (wsCtx) {
|
|
1366
1486
|
try {
|
|
1367
|
-
|
|
1487
|
+
const sessionsResult = await mcpQuery("agent.recentSessions", { limit: 3 });
|
|
1488
|
+
priorSessions = sessionsResult?.sessions ?? [];
|
|
1489
|
+
recoveryBlock = sessionsResult?.recoveryBlock ?? null;
|
|
1368
1490
|
} catch {
|
|
1369
1491
|
}
|
|
1370
1492
|
}
|
|
1493
|
+
const { all: sessionEntryIds, lastSessionOnly } = extractSessionEntryIds(priorSessions);
|
|
1371
1494
|
let orientEntries = null;
|
|
1372
1495
|
try {
|
|
1373
|
-
|
|
1496
|
+
const orientArgs = {};
|
|
1497
|
+
if (task) orientArgs.task = task;
|
|
1498
|
+
if (sessionEntryIds.length > 0) orientArgs.sessionEntryIds = sessionEntryIds;
|
|
1499
|
+
if (lastSessionOnly.length > 0) orientArgs.lastSessionEntryIds = lastSessionOnly;
|
|
1500
|
+
orientEntries = await mcpQuery("chain.getOrientEntries", orientArgs);
|
|
1374
1501
|
} catch {
|
|
1375
1502
|
}
|
|
1376
1503
|
let openTensions = [];
|
|
@@ -1415,7 +1542,10 @@ function registerHealthTools(server) {
|
|
|
1415
1542
|
lines.push(`- \`${e.entryId ?? e._id}\` ${e.name}${tensionPart}`);
|
|
1416
1543
|
}
|
|
1417
1544
|
}
|
|
1418
|
-
if (
|
|
1545
|
+
if (recoveryBlock) {
|
|
1546
|
+
lines.push("");
|
|
1547
|
+
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
1548
|
+
} else if (priorSessions.length > 0) {
|
|
1419
1549
|
const last = priorSessions[0];
|
|
1420
1550
|
const date = new Date(last.startedAt).toISOString().split("T")[0];
|
|
1421
1551
|
const created = Array.isArray(last.entriesCreated) ? last.entriesCreated.length : last.entriesCreated ?? 0;
|
|
@@ -1492,6 +1622,31 @@ function registerHealthTools(server) {
|
|
|
1492
1622
|
lines.push(`${betLine} ${sc.activeTensionCount} open tension(s).`);
|
|
1493
1623
|
lines.push("");
|
|
1494
1624
|
}
|
|
1625
|
+
if (orientEntries?.continuingFrom && orientEntries.continuingFrom.length > 0) {
|
|
1626
|
+
lines.push("## Continuing from");
|
|
1627
|
+
lines.push("_Prior-session entries most relevant to your task._");
|
|
1628
|
+
lines.push("");
|
|
1629
|
+
for (const e of orientEntries.continuingFrom) {
|
|
1630
|
+
const id = e.entryId ?? e.name;
|
|
1631
|
+
const type = e.canonicalKey ?? "generic";
|
|
1632
|
+
const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
|
|
1633
|
+
lines.push(`- \`${id}\` (score ${e.score}) [${type}]${coll} \u2014 ${e.name}`);
|
|
1634
|
+
if (e.reasoning) lines.push(` _${e.reasoning}_`);
|
|
1635
|
+
}
|
|
1636
|
+
lines.push("");
|
|
1637
|
+
}
|
|
1638
|
+
if (orientEntries?.lastSessionTouched && orientEntries.lastSessionTouched.length > 0) {
|
|
1639
|
+
lines.push("## Last session touched");
|
|
1640
|
+
lines.push("_Entries created or modified in your most recent session._");
|
|
1641
|
+
lines.push("");
|
|
1642
|
+
for (const e of orientEntries.lastSessionTouched) {
|
|
1643
|
+
const id = e.entryId ?? e.name;
|
|
1644
|
+
const type = e.canonicalKey ?? "generic";
|
|
1645
|
+
const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
|
|
1646
|
+
lines.push(`- \`${id}\` [${type}]${coll} \u2014 ${e.name}`);
|
|
1647
|
+
}
|
|
1648
|
+
lines.push("");
|
|
1649
|
+
}
|
|
1495
1650
|
if (orientEntries?.taskContext && orientEntries.taskContext.context.length > 0) {
|
|
1496
1651
|
const tc = orientEntries.taskContext;
|
|
1497
1652
|
lines.push("## Task Context");
|
|
@@ -1561,10 +1716,10 @@ function registerHealthTools(server) {
|
|
|
1561
1716
|
}
|
|
1562
1717
|
const plannedWork = await queryPlannedWork();
|
|
1563
1718
|
if (hasPlannedWork(plannedWork)) {
|
|
1564
|
-
lines.push(...buildPlannedWorkSection(plannedWork, priorSessions));
|
|
1719
|
+
lines.push(...buildPlannedWorkSection(plannedWork, priorSessions, recoveryBlock));
|
|
1565
1720
|
} else {
|
|
1566
1721
|
const briefingItems = [];
|
|
1567
|
-
if (priorSessions.length > 0) {
|
|
1722
|
+
if (priorSessions.length > 0 && !recoveryBlock) {
|
|
1568
1723
|
const last = priorSessions[0];
|
|
1569
1724
|
const date = new Date(last.startedAt).toISOString().split("T")[0];
|
|
1570
1725
|
const created = Array.isArray(last.entriesCreated) ? last.entriesCreated.length : last.entriesCreated ?? 0;
|
|
@@ -1581,6 +1736,10 @@ function registerHealthTools(server) {
|
|
|
1581
1736
|
}
|
|
1582
1737
|
lines.push("");
|
|
1583
1738
|
}
|
|
1739
|
+
if (recoveryBlock) {
|
|
1740
|
+
lines.push("");
|
|
1741
|
+
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
1742
|
+
}
|
|
1584
1743
|
}
|
|
1585
1744
|
lines.push("What would you like to work on?");
|
|
1586
1745
|
lines.push("");
|
|
@@ -4030,8 +4189,11 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
|
4030
4189
|
const wsFullCtx = await getWorkspaceContext();
|
|
4031
4190
|
const { ageDays, isNeglected } = computeWorkspaceAge(wsFullCtx.createdAt);
|
|
4032
4191
|
let priorSessions = [];
|
|
4192
|
+
let recoveryBlock = null;
|
|
4033
4193
|
try {
|
|
4034
|
-
|
|
4194
|
+
const sessionsResult = await mcpQuery("agent.recentSessions", { limit: 3 });
|
|
4195
|
+
priorSessions = sessionsResult?.sessions ?? [];
|
|
4196
|
+
recoveryBlock = sessionsResult?.recoveryBlock ?? null;
|
|
4035
4197
|
} catch {
|
|
4036
4198
|
}
|
|
4037
4199
|
let openTensions = [];
|
|
@@ -4086,10 +4248,10 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
|
4086
4248
|
const plannedWork = await queryPlannedWork();
|
|
4087
4249
|
if (hasPlannedWork(plannedWork)) {
|
|
4088
4250
|
lines.push("");
|
|
4089
|
-
lines.push(...buildPlannedWorkSection(plannedWork, priorSessions));
|
|
4251
|
+
lines.push(...buildPlannedWorkSection(plannedWork, priorSessions, recoveryBlock));
|
|
4090
4252
|
} else {
|
|
4091
4253
|
const briefingItems = [];
|
|
4092
|
-
if (priorSessions.length > 0) {
|
|
4254
|
+
if (priorSessions.length > 0 && !recoveryBlock) {
|
|
4093
4255
|
const last = priorSessions[0];
|
|
4094
4256
|
const date = new Date(last.startedAt).toISOString().split("T")[0];
|
|
4095
4257
|
const created = Array.isArray(last.entriesCreated) ? last.entriesCreated.length : last.entriesCreated ?? 0;
|
|
@@ -4107,6 +4269,10 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
|
4107
4269
|
}
|
|
4108
4270
|
lines.push("");
|
|
4109
4271
|
}
|
|
4272
|
+
if (recoveryBlock) {
|
|
4273
|
+
lines.push("");
|
|
4274
|
+
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
4275
|
+
}
|
|
4110
4276
|
}
|
|
4111
4277
|
lines.push("What would you like to work on?");
|
|
4112
4278
|
lines.push("");
|
|
@@ -4692,4 +4858,4 @@ export {
|
|
|
4692
4858
|
SERVER_VERSION,
|
|
4693
4859
|
createProductBrainServer
|
|
4694
4860
|
};
|
|
4695
|
-
//# sourceMappingURL=chunk-
|
|
4861
|
+
//# sourceMappingURL=chunk-R4LVIGFQ.js.map
|