@cognistore/mcp-server 0.9.13 → 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 +335 -53
  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);
@@ -1510,28 +1588,34 @@ var KnowledgeSDK = class {
1510
1588
  };
1511
1589
 
1512
1590
  // src/server.ts
1513
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1591
+ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
1514
1592
  import { z as z2 } from "zod";
1515
1593
  var knowledgeTypeValues = ["decision", "pattern", "fix", "constraint", "gotcha"];
1516
1594
  var knowledgeStatusValues = ["draft", "active", "completed", "archived"];
1595
+ var READ_ONLY = { readOnlyHint: true, destructiveHint: false };
1596
+ var WRITE = { readOnlyHint: false, destructiveHint: false };
1597
+ var DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true };
1517
1598
  function createServer(sdk) {
1518
1599
  const server = new McpServer({
1519
1600
  name: "cognistore",
1520
- version: "0.1.0"
1601
+ version: "1.0.0"
1521
1602
  });
1603
+ let lastSearchResultIds = [];
1522
1604
  server.tool(
1523
1605
  "addKnowledge",
1524
- "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.",
1525
1607
  {
1526
- title: z2.string().describe("Short descriptive title for the knowledge entry"),
1527
- content: z2.string().describe("The knowledge content text to store"),
1528
- tags: z2.array(z2.string()).describe("Mandatory categorical tags for filtering"),
1529
- 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"),
1530
1612
  scope: z2.string().describe('Scope: "global" or "workspace:<project-name>"'),
1531
1613
  source: z2.string().describe("Source of the knowledge"),
1532
1614
  confidenceScore: z2.number().min(0).max(1).optional().describe("Confidence score 0-1"),
1533
- 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.")
1534
1617
  },
1618
+ WRITE,
1535
1619
  async (params) => {
1536
1620
  const result = await sdk.addKnowledge({
1537
1621
  title: params.title,
@@ -1543,12 +1627,69 @@ function createServer(sdk) {
1543
1627
  confidenceScore: params.confidenceScore,
1544
1628
  agentId: params.agentId
1545
1629
  });
1546
- 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) }] };
1547
1688
  }
1548
1689
  );
1549
1690
  server.tool(
1550
1691
  "getKnowledge",
1551
- "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.",
1552
1693
  {
1553
1694
  query: z2.string().describe("Natural language query to search for"),
1554
1695
  tags: z2.array(z2.string()).optional().describe("Optional tag filters"),
@@ -1557,6 +1698,7 @@ function createServer(sdk) {
1557
1698
  limit: z2.number().optional().describe("Max results (default: 10)"),
1558
1699
  threshold: z2.number().optional().describe("Min similarity 0-1 (default: 0.3)")
1559
1700
  },
1701
+ READ_ONLY,
1560
1702
  async (params) => {
1561
1703
  const results = await sdk.getKnowledge(params.query, {
1562
1704
  tags: params.tags,
@@ -1565,7 +1707,23 @@ function createServer(sdk) {
1565
1707
  limit: params.limit,
1566
1708
  threshold: params.threshold
1567
1709
  });
1568
- 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) }] };
1569
1727
  }
1570
1728
  );
1571
1729
  server.tool(
@@ -1581,6 +1739,7 @@ function createServer(sdk) {
1581
1739
  source: z2.string().optional().describe("New source"),
1582
1740
  confidenceScore: z2.number().min(0).max(1).optional().describe("New confidence score")
1583
1741
  },
1742
+ WRITE,
1584
1743
  async (params) => {
1585
1744
  const { id, ...updates } = params;
1586
1745
  const result = await sdk.updateKnowledge(id, {
@@ -1592,8 +1751,8 @@ function createServer(sdk) {
1592
1751
  source: updates.source,
1593
1752
  confidenceScore: updates.confidenceScore
1594
1753
  });
1595
- const text2 = result ? JSON.stringify(result, null, 2) : "Knowledge entry not found";
1596
- 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) }] };
1597
1756
  }
1598
1757
  );
1599
1758
  server.tool(
@@ -1602,17 +1761,18 @@ function createServer(sdk) {
1602
1761
  {
1603
1762
  id: z2.string().describe("UUID of the knowledge entry to delete")
1604
1763
  },
1764
+ DESTRUCTIVE,
1605
1765
  async (params) => {
1606
1766
  const deleted = await sdk.deleteKnowledge(params.id);
1607
- return {
1608
- content: [{ type: "text", text: deleted ? "Deleted successfully" : "Not found" }]
1609
- };
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 }) }] };
1610
1769
  }
1611
1770
  );
1612
1771
  server.tool(
1613
1772
  "listTags",
1614
1773
  "List all unique tags across all knowledge entries.",
1615
1774
  {},
1775
+ READ_ONLY,
1616
1776
  async () => {
1617
1777
  const tags = await sdk.listTags();
1618
1778
  return { content: [{ type: "text", text: JSON.stringify(tags) }] };
@@ -1622,6 +1782,7 @@ function createServer(sdk) {
1622
1782
  "healthCheck",
1623
1783
  "Check health of the knowledge base infrastructure (database, Ollama).",
1624
1784
  {},
1785
+ READ_ONLY,
1625
1786
  async () => {
1626
1787
  const health = await sdk.healthCheck();
1627
1788
  return { content: [{ type: "text", text: JSON.stringify(health, null, 2) }] };
@@ -1629,73 +1790,90 @@ function createServer(sdk) {
1629
1790
  );
1630
1791
  server.tool(
1631
1792
  "createPlan",
1632
- "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.",
1633
1794
  {
1634
1795
  title: z2.string().describe("Plan title (short, descriptive)"),
1635
1796
  content: z2.string().describe("Full plan content (steps, approach, considerations)"),
1636
1797
  tags: z2.array(z2.string()).describe("Tags for categorization"),
1637
1798
  scope: z2.string().describe('Scope: "global" or "workspace:<project-name>"'),
1638
1799
  source: z2.string().describe("Source/context of the plan"),
1639
- 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)"),
1640
1801
  tasks: z2.array(z2.object({
1641
1802
  description: z2.string(),
1642
1803
  priority: z2.enum(["low", "medium", "high"]).optional()
1643
- })).optional().describe("Initial tasks for the plan todo list")
1804
+ })).optional().describe("Tasks for the plan. ALWAYS include tasks for multi-step work.")
1644
1805
  },
1806
+ WRITE,
1645
1807
  async (params) => {
1808
+ const inputIds = /* @__PURE__ */ new Set([
1809
+ ...params.relatedKnowledgeIds || [],
1810
+ ...lastSearchResultIds
1811
+ ]);
1646
1812
  const result = await sdk.createPlan({
1647
1813
  title: params.title,
1648
1814
  content: params.content,
1649
1815
  tags: params.tags,
1650
1816
  scope: params.scope,
1651
1817
  source: params.source,
1652
- relatedKnowledgeIds: params.relatedKnowledgeIds,
1818
+ relatedKnowledgeIds: inputIds.size > 0 ? [...inputIds] : void 0,
1653
1819
  tasks: params.tasks
1654
1820
  });
1655
- 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) }] };
1656
1827
  }
1657
1828
  );
1658
1829
  server.tool(
1659
1830
  "updatePlan",
1660
- "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.",
1661
1832
  {
1662
1833
  planId: z2.string().describe("UUID of the plan to update"),
1663
1834
  title: z2.string().optional().describe("New title"),
1664
1835
  content: z2.string().optional().describe("New content"),
1665
1836
  tags: z2.array(z2.string()).optional().describe("New tags"),
1666
1837
  scope: z2.string().optional().describe("New scope"),
1667
- status: z2.enum(knowledgeStatusValues).optional().describe("New status"),
1838
+ status: z2.enum(knowledgeStatusValues).optional().describe("New status (usually auto-managed)"),
1668
1839
  source: z2.string().optional().describe("New source")
1669
1840
  },
1841
+ WRITE,
1670
1842
  async (params) => {
1671
1843
  const { planId, ...updates } = params;
1672
1844
  const result = sdk.updatePlan(planId, updates);
1673
- const text2 = result ? JSON.stringify(result, null, 2) : "Plan not found";
1674
- 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) }] };
1675
1847
  }
1676
1848
  );
1677
1849
  server.tool(
1678
1850
  "addPlanRelation",
1679
- '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.",
1680
1852
  {
1681
1853
  planId: z2.string().describe("UUID of the plan"),
1682
1854
  knowledgeId: z2.string().describe("UUID of the knowledge entry to link"),
1683
- 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')
1684
1856
  },
1857
+ WRITE,
1685
1858
  async (params) => {
1686
- sdk.addPlanRelation(params.planId, params.knowledgeId, params.relationType);
1687
- 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
+ }
1688
1865
  }
1689
1866
  );
1690
1867
  server.tool(
1691
1868
  "addPlanTask",
1692
- "Add a task to a plan todo list. Position is auto-calculated.",
1869
+ "Add a task to a plan. Position is auto-calculated.",
1693
1870
  {
1694
1871
  planId: z2.string().describe("UUID of the plan"),
1695
1872
  description: z2.string().describe("Task description"),
1696
1873
  priority: z2.enum(["low", "medium", "high"]).optional().describe("Priority (default: medium)"),
1697
1874
  notes: z2.string().optional().describe("Optional notes")
1698
1875
  },
1876
+ WRITE,
1699
1877
  async (params) => {
1700
1878
  const task = sdk.createPlanTask(params);
1701
1879
  return { content: [{ type: "text", text: JSON.stringify(task, null, 2) }] };
@@ -1703,7 +1881,7 @@ function createServer(sdk) {
1703
1881
  );
1704
1882
  server.tool(
1705
1883
  "updatePlanTask",
1706
- "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.",
1707
1885
  {
1708
1886
  taskId: z2.string().describe("UUID of the task"),
1709
1887
  status: z2.enum(["pending", "in_progress", "completed"]).optional().describe("New status"),
@@ -1711,22 +1889,126 @@ function createServer(sdk) {
1711
1889
  priority: z2.enum(["low", "medium", "high"]).optional().describe("New priority"),
1712
1890
  notes: z2.string().nullable().optional().describe("Notes about progress or blockers")
1713
1891
  },
1892
+ WRITE,
1714
1893
  async (params) => {
1715
1894
  const { taskId, ...updates } = params;
1716
- const task = sdk.updatePlanTask(taskId, updates);
1717
- const text2 = task ? JSON.stringify(task, null, 2) : "Task not found";
1718
- 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) }] };
1719
1929
  }
1720
1930
  );
1721
1931
  server.tool(
1722
1932
  "listPlanTasks",
1723
- "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.",
1724
1934
  {
1725
1935
  planId: z2.string().describe("UUID of the plan")
1726
1936
  },
1937
+ READ_ONLY,
1727
1938
  async (params) => {
1728
1939
  const tasks = sdk.listPlanTasks(params.planId);
1729
- 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) }] };
1947
+ }
1948
+ );
1949
+ server.resource(
1950
+ "knowledge-context",
1951
+ new ResourceTemplate("cognistore://context/{scope}", { list: void 0 }),
1952
+ { description: "Workspace-scoped knowledge base context with recent entries and active plans" },
1953
+ async (uri, variables) => {
1954
+ const scope = variables.scope || "global";
1955
+ const scopeFilter = scope === "global" ? void 0 : `workspace:${scope}`;
1956
+ let knowledgeSection = "";
1957
+ try {
1958
+ const results = await sdk.getKnowledge("*", { scope: scopeFilter, limit: 10, threshold: 0 });
1959
+ if (results.length > 0) {
1960
+ knowledgeSection = "## Recent Knowledge\n\n" + results.map(
1961
+ (r) => `- **${r.entry.title}** (${r.entry.type}, ${r.entry.scope})
1962
+ ${r.entry.content.slice(0, 200)}${r.entry.content.length > 200 ? "..." : ""}`
1963
+ ).join("\n\n");
1964
+ } else {
1965
+ knowledgeSection = "## Recent Knowledge\n\nNo entries found for this scope.";
1966
+ }
1967
+ } catch {
1968
+ knowledgeSection = "## Recent Knowledge\n\nUnable to fetch entries.";
1969
+ }
1970
+ let plansSection = "";
1971
+ try {
1972
+ const plans = sdk.listPlans(10, "active");
1973
+ const scopedPlans = scopeFilter ? plans.filter((p) => p.scope === scopeFilter || p.scope === "global") : plans;
1974
+ if (scopedPlans.length > 0) {
1975
+ const planEntries = scopedPlans.map((p) => {
1976
+ const tasks = sdk.listPlanTasks(p.id);
1977
+ const completed = tasks.filter((t) => t.status === "completed").length;
1978
+ return `- **${p.title}** (${p.status}, ${completed}/${tasks.length} tasks done)
1979
+ ${p.content.slice(0, 150)}${p.content.length > 150 ? "..." : ""}`;
1980
+ });
1981
+ plansSection = "## Active Plans\n\n" + planEntries.join("\n\n");
1982
+ } else {
1983
+ plansSection = "## Active Plans\n\nNo active plans for this scope.";
1984
+ }
1985
+ } catch {
1986
+ plansSection = "## Active Plans\n\nUnable to fetch plans.";
1987
+ }
1988
+ let tagsSection = "";
1989
+ try {
1990
+ const tags = await sdk.listTags();
1991
+ if (tags.length > 0) {
1992
+ tagsSection = `## Tags
1993
+
1994
+ ${tags.slice(0, 20).join(", ")}`;
1995
+ }
1996
+ } catch {
1997
+ }
1998
+ const content = `# CogniStore Context \u2014 ${scope}
1999
+
2000
+ ${knowledgeSection}
2001
+
2002
+ ${plansSection}
2003
+
2004
+ ${tagsSection}`.trim();
2005
+ return {
2006
+ contents: [{
2007
+ uri: uri.href,
2008
+ text: content,
2009
+ mimeType: "text/markdown"
2010
+ }]
2011
+ };
1730
2012
  }
1731
2013
  );
1732
2014
  return server;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cognistore/mcp-server",
3
- "version": "0.9.13",
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",