@cognistore/mcp-server 0.9.14 → 1.0.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.
Files changed (2) hide show
  1. package/dist/index.js +255 -57
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ var KnowledgeType;
21
21
  KnowledgeType2["FIX"] = "fix";
22
22
  KnowledgeType2["CONSTRAINT"] = "constraint";
23
23
  KnowledgeType2["GOTCHA"] = "gotcha";
24
+ KnowledgeType2["SYSTEM"] = "system";
24
25
  })(KnowledgeType || (KnowledgeType = {}));
25
26
  var KnowledgeStatus;
26
27
  (function(KnowledgeStatus2) {
@@ -134,7 +135,7 @@ __export(schema_exports, {
134
135
 
135
136
  // ../../packages/core/dist/db/schema/knowledge.js
136
137
  import { sqliteTable, text, integer, real, index } from "drizzle-orm/sqlite-core";
137
- var knowledgeTypeEnum = ["decision", "pattern", "fix", "constraint", "gotcha"];
138
+ var knowledgeTypeEnum = ["decision", "pattern", "fix", "constraint", "gotcha", "system"];
138
139
  var knowledgeEntries = sqliteTable("knowledge_entries", {
139
140
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
140
141
  title: text("title").notNull().default(""),
@@ -376,7 +377,7 @@ function createPlansEmbeddingsTable(sqlite) {
376
377
  }
377
378
 
378
379
  // ../../packages/core/dist/repositories/knowledge.repository.js
379
- import { eq, sql, and, or, isNull } from "drizzle-orm";
380
+ import { eq, ne, sql, and, or, isNull } from "drizzle-orm";
380
381
  var KnowledgeRepository = class {
381
382
  db;
382
383
  sqlite;
@@ -461,6 +462,7 @@ var KnowledgeRepository = class {
461
462
  if (options?.type) {
462
463
  conditions.push(eq(knowledgeEntries.type, options.type));
463
464
  }
465
+ conditions.push(ne(knowledgeEntries.type, "system"));
464
466
  conditions.push(or(isNull(knowledgeEntries.expiresAt), sql`${knowledgeEntries.expiresAt} > ${(/* @__PURE__ */ new Date()).toISOString()}`));
465
467
  const whereClause = and(...conditions);
466
468
  const entries = await this.db.select().from(knowledgeEntries).where(whereClause);
@@ -470,27 +472,23 @@ var KnowledgeRepository = class {
470
472
  })).filter((r) => r.similarity >= threshold).sort((a, b) => b.similarity - a.similarity).slice(0, limit);
471
473
  }
472
474
  async listRecent(limit = 20, filters) {
473
- const conditions = [];
475
+ const conditions = [ne(knowledgeEntries.type, "system")];
474
476
  if (filters?.type)
475
477
  conditions.push(sql`${knowledgeEntries.type} = ${filters.type}`);
476
478
  if (filters?.scope)
477
479
  conditions.push(sql`${knowledgeEntries.scope} = ${filters.scope}`);
478
- if (conditions.length === 0) {
479
- return this.db.select().from(knowledgeEntries).orderBy(sql`${knowledgeEntries.createdAt} DESC`).limit(limit);
480
- }
481
- const where = conditions.length === 1 ? conditions[0] : sql`${conditions[0]} AND ${conditions[1]}`;
482
- return this.db.select().from(knowledgeEntries).where(where).orderBy(sql`${knowledgeEntries.createdAt} DESC`).limit(limit);
480
+ return this.db.select().from(knowledgeEntries).where(and(...conditions)).orderBy(sql`${knowledgeEntries.createdAt} DESC`).limit(limit);
483
481
  }
484
482
  async listTags() {
485
- const result = await this.db.all(sql`SELECT DISTINCT value FROM knowledge_entries, json_each(knowledge_entries.tags)`);
483
+ const result = await this.db.all(sql`SELECT DISTINCT value FROM knowledge_entries, json_each(knowledge_entries.tags) WHERE knowledge_entries.type != 'system'`);
486
484
  return result.map((r) => r.value);
487
485
  }
488
486
  async topTags(limit = 10) {
489
- const result = await this.db.all(sql`SELECT value as tag, COUNT(*) as count FROM knowledge_entries, json_each(knowledge_entries.tags) GROUP BY value ORDER BY count DESC LIMIT ${limit}`);
487
+ const result = await this.db.all(sql`SELECT value as tag, COUNT(*) as count FROM knowledge_entries, json_each(knowledge_entries.tags) WHERE knowledge_entries.type != 'system' GROUP BY value ORDER BY count DESC LIMIT ${limit}`);
490
488
  return result;
491
489
  }
492
490
  async count() {
493
- const [result] = await this.db.select({ count: sql`count(*)` }).from(knowledgeEntries);
491
+ const [result] = await this.db.select({ count: sql`count(*)` }).from(knowledgeEntries).where(ne(knowledgeEntries.type, "system"));
494
492
  return Number(result.count);
495
493
  }
496
494
  async lastUpdatedAt() {
@@ -501,18 +499,18 @@ var KnowledgeRepository = class {
501
499
  const results = await this.db.select({
502
500
  type: knowledgeEntries.type,
503
501
  count: sql`count(*)`
504
- }).from(knowledgeEntries).groupBy(knowledgeEntries.type);
502
+ }).from(knowledgeEntries).where(ne(knowledgeEntries.type, "system")).groupBy(knowledgeEntries.type);
505
503
  return results.map((r) => ({ type: r.type, count: Number(r.count) }));
506
504
  }
507
505
  async countByScope() {
508
506
  const results = await this.db.select({
509
507
  scope: knowledgeEntries.scope,
510
508
  count: sql`count(*)`
511
- }).from(knowledgeEntries).groupBy(knowledgeEntries.scope);
509
+ }).from(knowledgeEntries).where(ne(knowledgeEntries.type, "system")).groupBy(knowledgeEntries.scope);
512
510
  return results.map((r) => ({ scope: r.scope, count: Number(r.count) }));
513
511
  }
514
512
  async listAll() {
515
- return this.db.select().from(knowledgeEntries).orderBy(sql`${knowledgeEntries.createdAt} DESC`);
513
+ return this.db.select().from(knowledgeEntries).where(ne(knowledgeEntries.type, "system")).orderBy(sql`${knowledgeEntries.createdAt} DESC`);
516
514
  }
517
515
  async listScopes() {
518
516
  const rows = this.sqlite.prepare(`SELECT DISTINCT scope FROM knowledge_entries
@@ -638,6 +636,14 @@ var KnowledgeRepository = class {
638
636
  getPlanTaskById(id) {
639
637
  return this.sqlite.prepare("SELECT * FROM plan_tasks WHERE id = ?").get(id) ?? null;
640
638
  }
639
+ getTaskPlanId(taskId) {
640
+ const row = this.sqlite.prepare("SELECT plan_id FROM plan_tasks WHERE id = ?").get(taskId);
641
+ return row?.plan_id ?? null;
642
+ }
643
+ countIncompleteTasks(planId) {
644
+ const row = this.sqlite.prepare("SELECT COUNT(*) as cnt FROM plan_tasks WHERE plan_id = ? AND status != 'completed'").get(planId);
645
+ return row?.cnt ?? 0;
646
+ }
641
647
  getPlanTaskStats() {
642
648
  const result = this.sqlite.prepare(`
643
649
  SELECT
@@ -766,6 +772,11 @@ var KnowledgeService = class {
766
772
  const errors = [];
767
773
  for (const id of ids) {
768
774
  try {
775
+ const entry = await this.repository.findById(id);
776
+ if (entry?.type === "system") {
777
+ errors.push(`Skipped ${id}: system knowledge cannot be deleted`);
778
+ continue;
779
+ }
769
780
  const result = await this.repository.delete(id);
770
781
  if (result) {
771
782
  deleted++;
@@ -858,7 +869,17 @@ var KnowledgeService = class {
858
869
  }
859
870
  updatePlan(id, updates) {
860
871
  const row = this.repository.updatePlan(id, updates);
861
- return row ? this.toPlan(row) : null;
872
+ if (!row)
873
+ return null;
874
+ if (updates.status === "completed") {
875
+ const tasks = this.repository.listPlanTasks(id);
876
+ for (const t of tasks) {
877
+ if (t.status !== "completed") {
878
+ this.repository.updatePlanTask(t.id, { status: "completed" });
879
+ }
880
+ }
881
+ }
882
+ return this.toPlan(row);
862
883
  }
863
884
  deletePlan(id) {
864
885
  return this.repository.deletePlan(id);
@@ -934,7 +955,38 @@ var KnowledgeService = class {
934
955
  }
935
956
  updatePlanTask(id, updates) {
936
957
  const row = this.repository.updatePlanTask(id, updates);
937
- return row ? this.toPlanTask(row) : null;
958
+ if (!row)
959
+ return null;
960
+ const task = this.toPlanTask(row);
961
+ const planId = this.repository.getTaskPlanId(id);
962
+ const autoActions = [];
963
+ if (planId) {
964
+ if (updates.status === "in_progress") {
965
+ const plan = this.repository.getPlanById(planId);
966
+ if (plan && (plan.status === "draft" || plan.status === "completed")) {
967
+ this.repository.updatePlan(planId, { status: "active" });
968
+ autoActions.push(`Plan auto-activated from ${plan.status} to active`);
969
+ }
970
+ }
971
+ if (updates.status === "completed") {
972
+ const incomplete = this.repository.countIncompleteTasks(planId);
973
+ if (incomplete === 0) {
974
+ this.repository.updatePlan(planId, { status: "completed" });
975
+ autoActions.push("Plan auto-completed \u2014 all tasks done");
976
+ }
977
+ }
978
+ }
979
+ const currentPlan = planId ? this.repository.getPlanById(planId) : null;
980
+ const allTasks = planId ? this.repository.listPlanTasks(planId) : [];
981
+ const completedCount = allTasks.filter((t) => t.status === "completed").length;
982
+ const progress = `${completedCount}/${allTasks.length} completed`;
983
+ return {
984
+ task,
985
+ planId: planId ?? "",
986
+ planStatus: currentPlan?.status ?? "unknown",
987
+ progress,
988
+ autoActions
989
+ };
938
990
  }
939
991
  deletePlanTask(id) {
940
992
  return this.repository.deletePlanTask(id);
@@ -1262,16 +1314,28 @@ var KnowledgeSDK = class {
1262
1314
  throw new ValidationError(`Invalid updates: ${parsed.error.message}`);
1263
1315
  }
1264
1316
  try {
1317
+ const existing = await this.service.getById(id);
1318
+ if (existing?.type === "system" && updates.type && updates.type !== "system") {
1319
+ throw new ValidationError("System knowledge type cannot be changed");
1320
+ }
1265
1321
  return await this.service.update(id, parsed.data);
1266
1322
  } catch (error) {
1323
+ if (error instanceof ValidationError)
1324
+ throw error;
1267
1325
  throw this.wrapError(error, "Failed to update knowledge");
1268
1326
  }
1269
1327
  }
1270
1328
  async deleteKnowledge(id) {
1271
1329
  this.ensureInitialized();
1272
1330
  try {
1331
+ const existing = await this.service.getById(id);
1332
+ if (existing?.type === "system") {
1333
+ throw new ValidationError("System knowledge cannot be deleted");
1334
+ }
1273
1335
  return await this.service.delete(id);
1274
1336
  } catch (error) {
1337
+ if (error instanceof ValidationError)
1338
+ throw error;
1275
1339
  throw this.wrapError(error, "Failed to delete knowledge");
1276
1340
  }
1277
1341
  }
@@ -1390,8 +1454,12 @@ var KnowledgeSDK = class {
1390
1454
  this.ensureInitialized();
1391
1455
  return this.service.listPlans(limit, status);
1392
1456
  }
1393
- addPlanRelation(planId, knowledgeId, relationType) {
1457
+ async addPlanRelation(planId, knowledgeId, relationType) {
1394
1458
  this.ensureInitialized();
1459
+ const entry = await this.service.getById(knowledgeId);
1460
+ if (entry?.type === "system") {
1461
+ return;
1462
+ }
1395
1463
  this.service.addPlanRelation(planId, knowledgeId, relationType);
1396
1464
  }
1397
1465
  async getPlanRelations(planId) {
@@ -1411,6 +1479,16 @@ var KnowledgeSDK = class {
1411
1479
  this.ensureInitialized();
1412
1480
  return this.service.updatePlanTask(id, updates);
1413
1481
  }
1482
+ updatePlanTasks(updates) {
1483
+ this.ensureInitialized();
1484
+ const results = [];
1485
+ for (const u of updates) {
1486
+ const result = this.service.updatePlanTask(u.taskId, { status: u.status, notes: u.notes });
1487
+ if (result)
1488
+ results.push(result);
1489
+ }
1490
+ return results;
1491
+ }
1414
1492
  deletePlanTask(id) {
1415
1493
  this.ensureInitialized();
1416
1494
  return this.service.deletePlanTask(id);
@@ -1520,20 +1598,22 @@ var DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true };
1520
1598
  function createServer(sdk) {
1521
1599
  const server = new McpServer({
1522
1600
  name: "cognistore",
1523
- version: "0.1.0"
1601
+ version: "1.0.0"
1524
1602
  });
1603
+ let lastSearchResultIds = [];
1525
1604
  server.tool(
1526
1605
  "addKnowledge",
1527
- "Store a new knowledge entry with semantic embedding. Content is vectorized for future semantic search.",
1606
+ "Store a knowledge entry. If you have an active plan, ALWAYS pass planId to auto-link as output.",
1528
1607
  {
1529
- title: z2.string().describe("Short descriptive title for the knowledge entry"),
1530
- content: z2.string().describe("The knowledge content text to store"),
1531
- tags: z2.array(z2.string()).describe("Mandatory categorical tags for filtering"),
1532
- type: z2.enum(knowledgeTypeValues).describe("Type of knowledge entry"),
1608
+ title: z2.string().describe("Short descriptive title"),
1609
+ content: z2.string().describe("The knowledge content text"),
1610
+ tags: z2.array(z2.string()).describe("Categorical tags for filtering"),
1611
+ type: z2.enum(knowledgeTypeValues).describe("Type: decision, pattern, fix, constraint, or gotcha"),
1533
1612
  scope: z2.string().describe('Scope: "global" or "workspace:<project-name>"'),
1534
1613
  source: z2.string().describe("Source of the knowledge"),
1535
1614
  confidenceScore: z2.number().min(0).max(1).optional().describe("Confidence score 0-1"),
1536
- agentId: z2.string().optional().describe("ID of the agent that created this")
1615
+ agentId: z2.string().optional().describe("ID of the agent that created this"),
1616
+ planId: z2.string().optional().describe("Plan ID to auto-link this knowledge as output. ALWAYS pass this if you have an active plan.")
1537
1617
  },
1538
1618
  WRITE,
1539
1619
  async (params) => {
@@ -1547,12 +1627,69 @@ function createServer(sdk) {
1547
1627
  confidenceScore: params.confidenceScore,
1548
1628
  agentId: params.agentId
1549
1629
  });
1550
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1630
+ let linked = false;
1631
+ let linkWarning = "";
1632
+ if (params.planId && result.type !== "system") {
1633
+ try {
1634
+ await sdk.addPlanRelation(params.planId, result.id, "output");
1635
+ linked = true;
1636
+ } catch (e) {
1637
+ linkWarning = e instanceof Error ? e.message : "Unknown linking error";
1638
+ }
1639
+ }
1640
+ const response = { entry: result };
1641
+ if (params.planId) {
1642
+ response.linked = linked;
1643
+ response.planId = params.planId;
1644
+ if (linkWarning) response.linkWarning = linkWarning;
1645
+ }
1646
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1647
+ }
1648
+ );
1649
+ server.tool(
1650
+ "addKnowledgeBatch",
1651
+ "Create multiple knowledge entries at once. Pass planId per entry for output linking. Reduces tool calls.",
1652
+ {
1653
+ entries: z2.array(z2.object({
1654
+ title: z2.string(),
1655
+ content: z2.string(),
1656
+ tags: z2.array(z2.string()),
1657
+ type: z2.enum(knowledgeTypeValues),
1658
+ scope: z2.string(),
1659
+ source: z2.string(),
1660
+ planId: z2.string().optional()
1661
+ })).describe("Array of knowledge entries to create")
1662
+ },
1663
+ WRITE,
1664
+ async (params) => {
1665
+ const results = [];
1666
+ for (const e of params.entries) {
1667
+ const entry = await sdk.addKnowledge({
1668
+ title: e.title,
1669
+ content: e.content,
1670
+ tags: e.tags,
1671
+ type: e.type,
1672
+ scope: e.scope,
1673
+ source: e.source
1674
+ });
1675
+ let linked = false;
1676
+ let linkWarning = "";
1677
+ if (e.planId && entry.type !== "system") {
1678
+ try {
1679
+ await sdk.addPlanRelation(e.planId, entry.id, "output");
1680
+ linked = true;
1681
+ } catch (err) {
1682
+ linkWarning = err instanceof Error ? err.message : "Unknown linking error";
1683
+ }
1684
+ }
1685
+ results.push({ entry, linked, ...e.planId ? { planId: e.planId } : {}, ...linkWarning ? { linkWarning } : {} });
1686
+ }
1687
+ return { content: [{ type: "text", text: JSON.stringify({ created: results.length, entries: results }, null, 2) }] };
1551
1688
  }
1552
1689
  );
1553
1690
  server.tool(
1554
1691
  "getKnowledge",
1555
- "Search knowledge semantically. When a specific scope is provided, global knowledge is always included.",
1692
+ "Search knowledge semantically. SAVE returned entry IDs \u2014 pass them as relatedKnowledgeIds when calling createPlan.",
1556
1693
  {
1557
1694
  query: z2.string().describe("Natural language query to search for"),
1558
1695
  tags: z2.array(z2.string()).optional().describe("Optional tag filters"),
@@ -1570,7 +1707,23 @@ function createServer(sdk) {
1570
1707
  limit: params.limit,
1571
1708
  threshold: params.threshold
1572
1709
  });
1573
- return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
1710
+ lastSearchResultIds = results.map((r) => r.entry.id);
1711
+ const response = { results };
1712
+ try {
1713
+ const activePlans = sdk.listPlans(1, "active");
1714
+ const draftPlans = sdk.listPlans(1, "draft");
1715
+ const currentPlan = activePlans[0] || draftPlans[0];
1716
+ if (currentPlan) {
1717
+ response.activePlan = {
1718
+ id: currentPlan.id,
1719
+ title: currentPlan.title,
1720
+ status: currentPlan.status,
1721
+ hint: "Pass this planId to addKnowledge calls for output linking."
1722
+ };
1723
+ }
1724
+ } catch {
1725
+ }
1726
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1574
1727
  }
1575
1728
  );
1576
1729
  server.tool(
@@ -1598,8 +1751,8 @@ function createServer(sdk) {
1598
1751
  source: updates.source,
1599
1752
  confidenceScore: updates.confidenceScore
1600
1753
  });
1601
- const text2 = result ? JSON.stringify(result, null, 2) : "Knowledge entry not found";
1602
- return { content: [{ type: "text", text: text2 }] };
1754
+ if (!result) return { content: [{ type: "text", text: JSON.stringify({ error: "not_found", type: "knowledge_entry", id }) }] };
1755
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1603
1756
  }
1604
1757
  );
1605
1758
  server.tool(
@@ -1611,9 +1764,8 @@ function createServer(sdk) {
1611
1764
  DESTRUCTIVE,
1612
1765
  async (params) => {
1613
1766
  const deleted = await sdk.deleteKnowledge(params.id);
1614
- return {
1615
- content: [{ type: "text", text: deleted ? "Deleted successfully" : "Not found" }]
1616
- };
1767
+ if (!deleted) return { content: [{ type: "text", text: JSON.stringify({ error: "not_found", type: "knowledge_entry", id: params.id }) }] };
1768
+ return { content: [{ type: "text", text: JSON.stringify({ deleted: true, id: params.id }) }] };
1617
1769
  }
1618
1770
  );
1619
1771
  server.tool(
@@ -1638,70 +1790,83 @@ function createServer(sdk) {
1638
1790
  );
1639
1791
  server.tool(
1640
1792
  "createPlan",
1641
- "Create a new plan in the knowledge base. Plans are the ONLY way to persist implementation plans \u2014 never use local files. Status starts as draft.",
1793
+ "Create a plan with tasks. Plan auto-activates when the first task starts. Returns planId \u2014 SAVE IT and pass to addKnowledge calls.",
1642
1794
  {
1643
1795
  title: z2.string().describe("Plan title (short, descriptive)"),
1644
1796
  content: z2.string().describe("Full plan content (steps, approach, considerations)"),
1645
1797
  tags: z2.array(z2.string()).describe("Tags for categorization"),
1646
1798
  scope: z2.string().describe('Scope: "global" or "workspace:<project-name>"'),
1647
1799
  source: z2.string().describe("Source/context of the plan"),
1648
- relatedKnowledgeIds: z2.array(z2.string()).optional().describe("IDs of knowledge entries consulted during planning (input relations)"),
1800
+ relatedKnowledgeIds: z2.array(z2.string()).optional().describe("IDs of knowledge entries consulted during planning (auto-linked as input)"),
1649
1801
  tasks: z2.array(z2.object({
1650
1802
  description: z2.string(),
1651
1803
  priority: z2.enum(["low", "medium", "high"]).optional()
1652
- })).optional().describe("Initial tasks for the plan todo list")
1804
+ })).optional().describe("Tasks for the plan. ALWAYS include tasks for multi-step work.")
1653
1805
  },
1654
1806
  WRITE,
1655
1807
  async (params) => {
1808
+ const inputIds = /* @__PURE__ */ new Set([
1809
+ ...params.relatedKnowledgeIds || [],
1810
+ ...lastSearchResultIds
1811
+ ]);
1656
1812
  const result = await sdk.createPlan({
1657
1813
  title: params.title,
1658
1814
  content: params.content,
1659
1815
  tags: params.tags,
1660
1816
  scope: params.scope,
1661
1817
  source: params.source,
1662
- relatedKnowledgeIds: params.relatedKnowledgeIds,
1818
+ relatedKnowledgeIds: inputIds.size > 0 ? [...inputIds] : void 0,
1663
1819
  tasks: params.tasks
1664
1820
  });
1665
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1821
+ lastSearchResultIds = [];
1822
+ const response = {
1823
+ ...result,
1824
+ reminder: `Your plan ID is "${result.id}". Pass planId: "${result.id}" to every addKnowledge call for output linking. Plan auto-activates when you start the first task.`
1825
+ };
1826
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1666
1827
  }
1667
1828
  );
1668
1829
  server.tool(
1669
1830
  "updatePlan",
1670
- "Update an existing plan. Use to change status (draft \u2192 active \u2192 completed \u2192 archived), title, content, tags, or scope.",
1831
+ "Update a plan. Status lifecycle: draft \u2192 active \u2192 completed. Plan auto-activates and auto-completes via task updates \u2014 usually you do not need to call this manually.",
1671
1832
  {
1672
1833
  planId: z2.string().describe("UUID of the plan to update"),
1673
1834
  title: z2.string().optional().describe("New title"),
1674
1835
  content: z2.string().optional().describe("New content"),
1675
1836
  tags: z2.array(z2.string()).optional().describe("New tags"),
1676
1837
  scope: z2.string().optional().describe("New scope"),
1677
- status: z2.enum(knowledgeStatusValues).optional().describe("New status"),
1838
+ status: z2.enum(knowledgeStatusValues).optional().describe("New status (usually auto-managed)"),
1678
1839
  source: z2.string().optional().describe("New source")
1679
1840
  },
1680
1841
  WRITE,
1681
1842
  async (params) => {
1682
1843
  const { planId, ...updates } = params;
1683
1844
  const result = sdk.updatePlan(planId, updates);
1684
- const text2 = result ? JSON.stringify(result, null, 2) : "Plan not found";
1685
- return { content: [{ type: "text", text: text2 }] };
1845
+ if (!result) return { content: [{ type: "text", text: JSON.stringify({ error: "not_found", type: "plan", id: planId }) }] };
1846
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1686
1847
  }
1687
1848
  );
1688
1849
  server.tool(
1689
1850
  "addPlanRelation",
1690
- 'Link a knowledge entry to a plan. Use "input" for entries consulted during planning, "output" for entries created/updated during execution.',
1851
+ "Link a knowledge entry to a plan. Input = consulted during planning, output = created during execution. Usually auto-handled \u2014 use only for manual linking.",
1691
1852
  {
1692
1853
  planId: z2.string().describe("UUID of the plan"),
1693
1854
  knowledgeId: z2.string().describe("UUID of the knowledge entry to link"),
1694
- relationType: z2.enum(["input", "output"]).describe('"input" = consulted during planning, "output" = created/updated during execution')
1855
+ relationType: z2.enum(["input", "output"]).describe('"input" = consulted, "output" = produced')
1695
1856
  },
1696
1857
  WRITE,
1697
1858
  async (params) => {
1698
- sdk.addPlanRelation(params.planId, params.knowledgeId, params.relationType);
1699
- return { content: [{ type: "text", text: JSON.stringify({ success: true, ...params }) }] };
1859
+ try {
1860
+ sdk.addPlanRelation(params.planId, params.knowledgeId, params.relationType);
1861
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, ...params }) }] };
1862
+ } catch (e) {
1863
+ return { content: [{ type: "text", text: JSON.stringify({ error: "link_failed", message: e instanceof Error ? e.message : "Unknown error", ...params }) }] };
1864
+ }
1700
1865
  }
1701
1866
  );
1702
1867
  server.tool(
1703
1868
  "addPlanTask",
1704
- "Add a task to a plan todo list. Position is auto-calculated.",
1869
+ "Add a task to a plan. Position is auto-calculated.",
1705
1870
  {
1706
1871
  planId: z2.string().describe("UUID of the plan"),
1707
1872
  description: z2.string().describe("Task description"),
@@ -1716,7 +1881,7 @@ function createServer(sdk) {
1716
1881
  );
1717
1882
  server.tool(
1718
1883
  "updatePlanTask",
1719
- "Update a plan task. Mark in_progress when starting, completed when done. Add notes for context.",
1884
+ "Update a task status. Plan auto-activates on first in_progress and auto-completes when all tasks are done.",
1720
1885
  {
1721
1886
  taskId: z2.string().describe("UUID of the task"),
1722
1887
  status: z2.enum(["pending", "in_progress", "completed"]).optional().describe("New status"),
@@ -1727,21 +1892,58 @@ function createServer(sdk) {
1727
1892
  WRITE,
1728
1893
  async (params) => {
1729
1894
  const { taskId, ...updates } = params;
1730
- const task = sdk.updatePlanTask(taskId, updates);
1731
- const text2 = task ? JSON.stringify(task, null, 2) : "Task not found";
1732
- return { content: [{ type: "text", text: text2 }] };
1895
+ const result = sdk.updatePlanTask(taskId, updates);
1896
+ if (!result) return { content: [{ type: "text", text: JSON.stringify({ error: "not_found", type: "plan_task", id: taskId }) }] };
1897
+ const response = {
1898
+ task: result.task,
1899
+ plan: { id: result.planId, status: result.planStatus, progress: result.progress },
1900
+ ...result.autoActions.length > 0 ? { autoActions: result.autoActions } : {},
1901
+ reminder: `Plan ID: "${result.planId}". Pass this planId to addKnowledge calls for output linking.`
1902
+ };
1903
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1904
+ }
1905
+ );
1906
+ server.tool(
1907
+ "updatePlanTasks",
1908
+ "Update multiple tasks at once. Reduces tool calls. Plan auto-activates and auto-completes automatically.",
1909
+ {
1910
+ updates: z2.array(z2.object({
1911
+ taskId: z2.string().describe("UUID of the task"),
1912
+ status: z2.enum(["pending", "in_progress", "completed"]).optional(),
1913
+ notes: z2.string().nullable().optional()
1914
+ })).describe("Array of task updates")
1915
+ },
1916
+ WRITE,
1917
+ async (params) => {
1918
+ const results = sdk.updatePlanTasks(params.updates);
1919
+ const allAutoActions = results.flatMap((r) => r.autoActions);
1920
+ const lastResult = results[results.length - 1];
1921
+ const response = {
1922
+ updated: results.length,
1923
+ tasks: results.map((r) => ({ id: r.task.id, status: r.task.status, description: r.task.description })),
1924
+ plan: lastResult ? { id: lastResult.planId, status: lastResult.planStatus, progress: lastResult.progress } : void 0,
1925
+ ...allAutoActions.length > 0 ? { autoActions: allAutoActions } : {},
1926
+ reminder: lastResult ? `Plan ID: "${lastResult.planId}". Pass this planId to addKnowledge calls.` : void 0
1927
+ };
1928
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1733
1929
  }
1734
1930
  );
1735
1931
  server.tool(
1736
1932
  "listPlanTasks",
1737
- "List all tasks for a plan, ordered by position. Use to check progress or resume work.",
1933
+ "List all tasks for a plan, ordered by position. Shows progress.",
1738
1934
  {
1739
1935
  planId: z2.string().describe("UUID of the plan")
1740
1936
  },
1741
1937
  READ_ONLY,
1742
1938
  async (params) => {
1743
1939
  const tasks = sdk.listPlanTasks(params.planId);
1744
- return { content: [{ type: "text", text: JSON.stringify(tasks, null, 2) }] };
1940
+ const completed = tasks.filter((t) => t.status === "completed").length;
1941
+ const response = {
1942
+ tasks,
1943
+ progress: `${completed}/${tasks.length} completed`,
1944
+ reminder: `Plan ID: "${params.planId}". Pass this planId to addKnowledge calls for output linking.`
1945
+ };
1946
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1745
1947
  }
1746
1948
  );
1747
1949
  server.resource(
@@ -1753,11 +1955,7 @@ function createServer(sdk) {
1753
1955
  const scopeFilter = scope === "global" ? void 0 : `workspace:${scope}`;
1754
1956
  let knowledgeSection = "";
1755
1957
  try {
1756
- const results = await sdk.getKnowledge("*", {
1757
- scope: scopeFilter,
1758
- limit: 10,
1759
- threshold: 0
1760
- });
1958
+ const results = await sdk.getKnowledge("*", { scope: scopeFilter, limit: 10, threshold: 0 });
1761
1959
  if (results.length > 0) {
1762
1960
  knowledgeSection = "## Recent Knowledge\n\n" + results.map(
1763
1961
  (r) => `- **${r.entry.title}** (${r.entry.type}, ${r.entry.scope})
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cognistore/mcp-server",
3
- "version": "0.9.14",
3
+ "version": "1.0.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "MCP server for CogniStore — integrates with Claude Code and GitHub Copilot",