@productbrain/mcp 0.0.1-beta.26 → 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.
@@ -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-EVF3CYZN.js";
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-NX4QDLLY.js");
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 {
@@ -1240,6 +1320,27 @@ function buildSessionSummary(log) {
1240
1320
  }
1241
1321
  return lines.join("\n");
1242
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
+ }
1243
1344
  function registerHealthTools(server) {
1244
1345
  server.registerTool(
1245
1346
  "health",
@@ -1389,9 +1490,14 @@ function registerHealthTools(server) {
1389
1490
  } catch {
1390
1491
  }
1391
1492
  }
1493
+ const { all: sessionEntryIds, lastSessionOnly } = extractSessionEntryIds(priorSessions);
1392
1494
  let orientEntries = null;
1393
1495
  try {
1394
- orientEntries = await mcpQuery("chain.getOrientEntries", task ? { task } : {});
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);
1395
1501
  } catch {
1396
1502
  }
1397
1503
  let openTensions = [];
@@ -1516,6 +1622,31 @@ function registerHealthTools(server) {
1516
1622
  lines.push(`${betLine} ${sc.activeTensionCount} open tension(s).`);
1517
1623
  lines.push("");
1518
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
+ }
1519
1650
  if (orientEntries?.taskContext && orientEntries.taskContext.context.length > 0) {
1520
1651
  const tc = orientEntries.taskContext;
1521
1652
  lines.push("## Task Context");
@@ -4727,4 +4858,4 @@ export {
4727
4858
  SERVER_VERSION,
4728
4859
  createProductBrainServer
4729
4860
  };
4730
- //# sourceMappingURL=chunk-PVWT5LIB.js.map
4861
+ //# sourceMappingURL=chunk-R4LVIGFQ.js.map