@cognistore/mcp-server 2.2.3 → 2.3.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 +150 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -74,7 +74,8 @@ var createKnowledgeSchema = z.object({
74
74
  confidenceScore: z.number().min(0).max(1).optional().default(1),
75
75
  expiresAt: z.date().nullable().optional().default(null),
76
76
  relatedIds: z.array(z.string().uuid()).nullable().optional().default(null),
77
- agentId: z.string().nullable().optional().default(null)
77
+ agentId: z.string().max(64).nullable().optional().default(null),
78
+ platform: z.string().max(64).nullable().optional().default(null)
78
79
  });
79
80
  var updateKnowledgeSchema = z.object({
80
81
  title: z.string().min(1).optional(),
@@ -86,7 +87,8 @@ var updateKnowledgeSchema = z.object({
86
87
  confidenceScore: z.number().min(0).max(1).optional(),
87
88
  expiresAt: z.date().nullable().optional(),
88
89
  relatedIds: z.array(z.string().uuid()).nullable().optional(),
89
- agentId: z.string().nullable().optional()
90
+ agentId: z.string().max(64).nullable().optional(),
91
+ platform: z.string().max(64).nullable().optional()
90
92
  });
91
93
  var searchOptionsSchema = z.object({
92
94
  tags: z.array(z.string()).optional(),
@@ -105,6 +107,8 @@ var createPlanSchema = z.object({
105
107
  source: z.string().min(1, "Source is required"),
106
108
  status: knowledgeStatusSchema.optional().default(KnowledgeStatus.DRAFT),
107
109
  planFilePath: z.string().min(1).nullable().optional(),
110
+ agentId: z.string().max(64).nullable().optional(),
111
+ platform: z.string().max(64).nullable().optional(),
108
112
  tasks: z.array(z.object({
109
113
  description: z.string().min(1),
110
114
  priority: z.enum(["low", "medium", "high"]).optional()
@@ -117,7 +121,9 @@ var updatePlanSchema = z.object({
117
121
  scope: scopeSchema.optional(),
118
122
  status: knowledgeStatusSchema.optional(),
119
123
  source: z.string().min(1).optional(),
120
- planFilePath: z.string().min(1).nullable().optional()
124
+ planFilePath: z.string().min(1).nullable().optional(),
125
+ agentId: z.string().max(64).nullable().optional(),
126
+ platform: z.string().max(64).nullable().optional()
121
127
  });
122
128
  var createPlanTaskSchema = z.object({
123
129
  planId: z.string().min(1),
@@ -151,7 +157,8 @@ var importKnowledgeEntrySchema = z.object({
151
157
  confidenceScore: z.number().min(0).max(1).optional(),
152
158
  expiresAt: z.string().max(64).nullable().optional(),
153
159
  relatedIds: z.array(z.string().max(64)).nullable().optional(),
154
- agentId: z.string().max(200).nullable().optional()
160
+ agentId: z.string().max(200).nullable().optional(),
161
+ platform: z.string().max(200).nullable().optional()
155
162
  });
156
163
  var importSchema = z.object({
157
164
  include: z.array(z.string().max(50)).max(10).optional().default([]),
@@ -192,11 +199,13 @@ var knowledgeEntries = sqliteTable("knowledge_entries", {
192
199
  confidenceScore: real("confidence_score").notNull().default(1),
193
200
  relatedIds: text("related_ids", { mode: "json" }).$type(),
194
201
  agentId: text("agent_id"),
202
+ platform: text("platform"),
195
203
  createdAt: text("created_at").notNull().$defaultFn(() => (/* @__PURE__ */ new Date()).toISOString()),
196
204
  updatedAt: text("updated_at").notNull().$defaultFn(() => (/* @__PURE__ */ new Date()).toISOString())
197
205
  }, (table) => [
198
206
  index("idx_type").on(table.type),
199
- index("idx_scope").on(table.scope)
207
+ index("idx_scope").on(table.scope),
208
+ index("idx_knowledge_platform").on(table.platform)
200
209
  ]);
201
210
 
202
211
  // ../../packages/core/dist/db/schema/sqlite-vec.js
@@ -437,6 +446,16 @@ ALTER TABLE plans ADD COLUMN plan_file_path TEXT;
437
446
  `,
438
447
  "2.1.0": `
439
448
  CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_fts USING fts5(id UNINDEXED, title, content, tags);
449
+ `,
450
+ // v2.3.0: provenance tracking — platform (auto) + agent_id (caller) on entries and plans.
451
+ // knowledge_entries.agent_id already exists since 0.8.0; only platform is new there.
452
+ "2.3.0": `
453
+ ALTER TABLE knowledge_entries ADD COLUMN platform TEXT;
454
+ ALTER TABLE plans ADD COLUMN agent_id TEXT;
455
+ ALTER TABLE plans ADD COLUMN platform TEXT;
456
+ CREATE INDEX IF NOT EXISTS idx_knowledge_platform ON knowledge_entries(platform);
457
+ CREATE INDEX IF NOT EXISTS idx_plans_platform ON plans(platform);
458
+ CREATE INDEX IF NOT EXISTS idx_plans_agent ON plans(agent_id);
440
459
  `
441
460
  };
442
461
  function runMigrations(sqlite, migrationsDir) {
@@ -581,7 +600,7 @@ function createPlansEmbeddingsTable(sqlite, dimensions = DEFAULT_EMBEDDING_DIMEN
581
600
 
582
601
  // ../../packages/core/dist/repositories/knowledge.repository.js
583
602
  import { eq, ne, sql, and, or, isNull } from "drizzle-orm";
584
- var OPERATIONS_RETENTION_DAYS = 30;
603
+ var OPERATIONS_RETENTION_DAYS = 800;
585
604
  var KnowledgeRepository = class _KnowledgeRepository {
586
605
  db;
587
606
  sqlite;
@@ -603,8 +622,8 @@ var KnowledgeRepository = class _KnowledgeRepository {
603
622
  const now = (/* @__PURE__ */ new Date()).toISOString();
604
623
  const insertTxn = this.sqlite.transaction(() => {
605
624
  this.sqlite.prepare(`INSERT INTO knowledge_entries
606
- (id, title, content, tags, type, scope, source, version, expires_at, confidence_score, related_ids, agent_id, created_at, updated_at)
607
- VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)`).run(id, input.title, input.content, JSON.stringify(input.tags ?? []), input.type, input.scope, input.source, input.expiresAt ? input.expiresAt.toISOString() : null, input.confidenceScore ?? 1, input.relatedIds ? JSON.stringify(input.relatedIds) : null, input.agentId ?? null, now, now);
625
+ (id, title, content, tags, type, scope, source, version, expires_at, confidence_score, related_ids, agent_id, platform, created_at, updated_at)
626
+ VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?)`).run(id, input.title, input.content, JSON.stringify(input.tags ?? []), input.type, input.scope, input.source, input.expiresAt ? input.expiresAt.toISOString() : null, input.confidenceScore ?? 1, input.relatedIds ? JSON.stringify(input.relatedIds) : null, input.agentId ?? null, input.platform ?? null, now, now);
608
627
  insertEmbedding(this.sqlite, id, input.embedding);
609
628
  try {
610
629
  insertFts(this.sqlite, { id, title: input.title, content: input.content, tags: ftsTags(input.tags) });
@@ -712,6 +731,12 @@ var KnowledgeRepository = class _KnowledgeRepository {
712
731
  conditions.push(sql`${knowledgeEntries.type} = ${filters.type}`);
713
732
  if (filters?.scope)
714
733
  conditions.push(sql`${knowledgeEntries.scope} = ${filters.scope}`);
734
+ if (filters?.agent) {
735
+ conditions.push(filters.agent === "unspecified" ? isNull(knowledgeEntries.agentId) : sql`${knowledgeEntries.agentId} = ${filters.agent}`);
736
+ }
737
+ if (filters?.platform) {
738
+ conditions.push(filters.platform === "unknown" ? or(isNull(knowledgeEntries.platform), sql`${knowledgeEntries.platform} = 'unknown'`) : sql`${knowledgeEntries.platform} = ${filters.platform}`);
739
+ }
715
740
  if (filters?.tags && filters.tags.length > 0) {
716
741
  const tagConditions = filters.tags.map((tag) => sql`EXISTS (SELECT 1 FROM json_each(${knowledgeEntries.tags}) WHERE value = ${tag})`);
717
742
  conditions.push(or(...tagConditions));
@@ -962,6 +987,39 @@ var KnowledgeRepository = class _KnowledgeRepository {
962
987
  }).from(knowledgeEntries).where(and(...conditions)).groupBy(knowledgeEntries.scope);
963
988
  return results.map((r) => ({ scope: r.scope, count: Number(r.count) }));
964
989
  }
990
+ /**
991
+ * Count entries by the agent that created them. agent_id is caller-provided and
992
+ * NULL when not passed; COALESCE collapses NULL into a single "unspecified"
993
+ * bucket (the chart label the frontend round-trips back to IS NULL on filter).
994
+ */
995
+ async countByAgent(opts = {}) {
996
+ const { from, to } = opts;
997
+ const conditions = [ne(knowledgeEntries.type, "system")];
998
+ if (from && to) {
999
+ conditions.push(sql`${knowledgeEntries.createdAt} >= ${from}`);
1000
+ conditions.push(sql`${knowledgeEntries.createdAt} < ${to}`);
1001
+ }
1002
+ const agentLabel = sql`COALESCE(${knowledgeEntries.agentId}, 'unspecified')`;
1003
+ const results = await this.db.select({ agent: agentLabel, count: sql`count(*)` }).from(knowledgeEntries).where(and(...conditions)).groupBy(agentLabel);
1004
+ return results.map((r) => ({ agent: r.agent, count: Number(r.count) }));
1005
+ }
1006
+ /**
1007
+ * Count entries by the platform that created them. platform is NULL for
1008
+ * pre-2.3.0 / dashboard-created rows and the literal "unknown" for MCP rows
1009
+ * whose client couldn't be resolved; COALESCE merges both into one "unknown"
1010
+ * bucket so they don't render as two slices.
1011
+ */
1012
+ async countByPlatform(opts = {}) {
1013
+ const { from, to } = opts;
1014
+ const conditions = [ne(knowledgeEntries.type, "system")];
1015
+ if (from && to) {
1016
+ conditions.push(sql`${knowledgeEntries.createdAt} >= ${from}`);
1017
+ conditions.push(sql`${knowledgeEntries.createdAt} < ${to}`);
1018
+ }
1019
+ const platformLabel = sql`COALESCE(${knowledgeEntries.platform}, 'unknown')`;
1020
+ const results = await this.db.select({ platform: platformLabel, count: sql`count(*)` }).from(knowledgeEntries).where(and(...conditions)).groupBy(platformLabel);
1021
+ return results.map((r) => ({ platform: r.platform, count: Number(r.count) }));
1022
+ }
965
1023
  async listAll() {
966
1024
  return this.db.select().from(knowledgeEntries).where(ne(knowledgeEntries.type, "system")).orderBy(sql`${knowledgeEntries.createdAt} DESC`);
967
1025
  }
@@ -976,7 +1034,7 @@ var KnowledgeRepository = class _KnowledgeRepository {
976
1034
  createPlan(input) {
977
1035
  const id = crypto.randomUUID();
978
1036
  const now = (/* @__PURE__ */ new Date()).toISOString();
979
- this.sqlite.prepare("INSERT INTO plans (id, title, content, tags, scope, status, source, plan_file_path, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(id, input.title, input.content, JSON.stringify(input.tags), input.scope, input.status ?? "draft", input.source, input.planFilePath ?? null, now, now);
1037
+ this.sqlite.prepare("INSERT INTO plans (id, title, content, tags, scope, status, source, plan_file_path, agent_id, platform, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(id, input.title, input.content, JSON.stringify(input.tags), input.scope, input.status ?? "draft", input.source, input.planFilePath ?? null, input.agentId ?? null, input.platform ?? null, now, now);
980
1038
  try {
981
1039
  insertPlanEmbedding(this.sqlite, id, input.embedding);
982
1040
  } catch {
@@ -1248,6 +1306,28 @@ var KnowledgeRepository = class _KnowledgeRepository {
1248
1306
  }
1249
1307
  return Object.entries(map).map(([date, counts]) => ({ date, ...counts }));
1250
1308
  }
1309
+ /** Plans created per day, zero-filled for every day in the window (mirrors getOperationsByDay). */
1310
+ getPlansByDay(days = 15) {
1311
+ const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString();
1312
+ const rows = this.sqlite.prepare(`
1313
+ SELECT date(created_at) as date, COUNT(*) as count
1314
+ FROM plans
1315
+ WHERE created_at >= ?
1316
+ GROUP BY date(created_at)
1317
+ ORDER BY date(created_at)
1318
+ `).all(cutoff);
1319
+ const map = {};
1320
+ const now = /* @__PURE__ */ new Date();
1321
+ for (let i = days - 1; i >= 0; i--) {
1322
+ const d = new Date(now);
1323
+ d.setDate(d.getDate() - i);
1324
+ map[d.toISOString().split("T")[0]] = 0;
1325
+ }
1326
+ for (const row of rows) {
1327
+ map[row.date] = row.count;
1328
+ }
1329
+ return Object.entries(map).map(([date, count]) => ({ date, count }));
1330
+ }
1251
1331
  cleanupOldOperations() {
1252
1332
  const cutoff = new Date(Date.now() - OPERATIONS_RETENTION_DAYS * 24 * 60 * 60 * 1e3).toISOString();
1253
1333
  return this.sqlite.prepare("DELETE FROM operations_log WHERE created_at < ?").run(cutoff).changes;
@@ -1364,7 +1444,13 @@ var KnowledgeService = class {
1364
1444
  });
1365
1445
  if (similar.length > 0) {
1366
1446
  const existing = similar[0].entry;
1367
- const updated = await this.repository.update(existing.id, { ...rest, embedding });
1447
+ const { platform, agentId, ...keep } = rest;
1448
+ const prov = {};
1449
+ if (platform != null)
1450
+ prov.platform = platform;
1451
+ if (agentId != null)
1452
+ prov.agentId = agentId;
1453
+ const updated = await this.repository.update(existing.id, { ...keep, ...prov, embedding });
1368
1454
  this.logOp("write");
1369
1455
  return { ...this.toKnowledgeEntry(updated), deduplicated: true };
1370
1456
  }
@@ -1749,6 +1835,12 @@ var KnowledgeService = class {
1749
1835
  async countByScope(opts = {}) {
1750
1836
  return this.repository.countByScope(opts);
1751
1837
  }
1838
+ async countByAgent(opts = {}) {
1839
+ return this.repository.countByAgent(opts);
1840
+ }
1841
+ async countByPlatform(opts = {}) {
1842
+ return this.repository.countByPlatform(opts);
1843
+ }
1752
1844
  // ─── Plans (separate entity) ────────────────────────────────
1753
1845
  async createPlan(input) {
1754
1846
  const { tasks, skipDedup, ...planInput } = input;
@@ -1996,6 +2088,9 @@ var KnowledgeService = class {
1996
2088
  getOperationsByDay(days = 15) {
1997
2089
  return this.repository.getOperationsByDay(days);
1998
2090
  }
2091
+ getPlansByDay(days = 15) {
2092
+ return this.repository.getPlansByDay(days);
2093
+ }
1999
2094
  cleanupOldOperations() {
2000
2095
  return this.repository.cleanupOldOperations();
2001
2096
  }
@@ -2017,7 +2112,8 @@ var KnowledgeService = class {
2017
2112
  expiresAt: row.expiresAt ? new Date(row.expiresAt) : null,
2018
2113
  confidenceScore: row.confidenceScore,
2019
2114
  relatedIds: row.relatedIds ? Array.isArray(row.relatedIds) ? row.relatedIds : JSON.parse(row.relatedIds) : null,
2020
- agentId: row.agentId,
2115
+ agentId: row.agentId ?? row.agent_id ?? null,
2116
+ platform: row.platform ?? null,
2021
2117
  createdAt: new Date(row.createdAt ?? row.created_at),
2022
2118
  updatedAt: new Date(row.updatedAt ?? row.updated_at)
2023
2119
  };
@@ -2032,6 +2128,8 @@ var KnowledgeService = class {
2032
2128
  status: row.status,
2033
2129
  source: row.source ?? "",
2034
2130
  planFilePath: row.plan_file_path ?? row.planFilePath ?? null,
2131
+ agentId: row.agent_id ?? row.agentId ?? null,
2132
+ platform: row.platform ?? null,
2035
2133
  createdAt: new Date(row.created_at ?? row.createdAt),
2036
2134
  updatedAt: new Date(row.updated_at ?? row.updatedAt)
2037
2135
  };
@@ -3578,6 +3676,22 @@ var KnowledgeSDK = class {
3578
3676
  throw this.wrapError(error, "Failed to count by scope");
3579
3677
  }
3580
3678
  }
3679
+ async countByAgent(opts = {}) {
3680
+ this.ensureInitialized();
3681
+ try {
3682
+ return await this.service.countByAgent(opts);
3683
+ } catch (error) {
3684
+ throw this.wrapError(error, "Failed to count by agent");
3685
+ }
3686
+ }
3687
+ async countByPlatform(opts = {}) {
3688
+ this.ensureInitialized();
3689
+ try {
3690
+ return await this.service.countByPlatform(opts);
3691
+ } catch (error) {
3692
+ throw this.wrapError(error, "Failed to count by platform");
3693
+ }
3694
+ }
3581
3695
  async getStats() {
3582
3696
  this.ensureInitialized();
3583
3697
  try {
@@ -3733,6 +3847,10 @@ var KnowledgeSDK = class {
3733
3847
  this.ensureInitialized();
3734
3848
  return this.service.getOperationsByDay(days);
3735
3849
  }
3850
+ getPlansByDay(days = 15) {
3851
+ this.ensureInitialized();
3852
+ return this.service.getPlansByDay(days);
3853
+ }
3736
3854
  cleanupOldOperations() {
3737
3855
  if (!this.initialized || !this.service)
3738
3856
  return 0;
@@ -3879,6 +3997,21 @@ function createServer(sdk) {
3879
3997
  version: "1.0.0"
3880
3998
  });
3881
3999
  let lastSearchResultIds = [];
4000
+ const PLATFORM_ALLOWLIST = /* @__PURE__ */ new Set(["claude-code", "copilot", "opencode"]);
4001
+ function normalizeProvenance(s) {
4002
+ if (typeof s !== "string") return void 0;
4003
+ const cleaned = Array.from(s).filter((c) => {
4004
+ const n = c.charCodeAt(0);
4005
+ return n >= 32 && n !== 127;
4006
+ }).join("").trim().slice(0, 64);
4007
+ return cleaned.length > 0 ? cleaned : void 0;
4008
+ }
4009
+ function resolvePlatform() {
4010
+ const raw = normalizeProvenance(process.env.COGNISTORE_PLATFORM) ?? normalizeProvenance(server.server.getClientVersion()?.name);
4011
+ if (!raw) return "unknown";
4012
+ const mapped = raw.toLowerCase() === "claude" ? "claude-code" : raw.toLowerCase();
4013
+ return PLATFORM_ALLOWLIST.has(mapped) ? mapped : "unknown";
4014
+ }
3882
4015
  const knowledgeEntrySchema = z3.object({
3883
4016
  title: z3.string().describe("Short descriptive title"),
3884
4017
  content: z3.string().describe("The knowledge content text"),
@@ -3887,7 +4020,7 @@ function createServer(sdk) {
3887
4020
  scope: z3.string().describe('Scope: "global" or "workspace:<project-name>"'),
3888
4021
  source: z3.string().describe("Source of the knowledge"),
3889
4022
  confidenceScore: z3.number().min(0).max(1).optional().describe("Confidence score 0-1"),
3890
- agentId: z3.string().optional().describe("ID of the agent that created this"),
4023
+ agentId: z3.string().optional().describe("Your agent/role name (e.g. 'documentation', 'code-reviewer') so entries can be summarized per agent. Pass it whenever you are a named/custom agent."),
3891
4024
  planId: z3.string().optional().describe("Plan ID to auto-link this knowledge as output. ALWAYS pass this if you have an active plan.")
3892
4025
  });
3893
4026
  async function createEntry(params) {
@@ -3899,7 +4032,8 @@ function createServer(sdk) {
3899
4032
  scope: params.scope,
3900
4033
  source: params.source,
3901
4034
  confidenceScore: params.confidenceScore,
3902
- agentId: params.agentId
4035
+ agentId: normalizeProvenance(params.agentId),
4036
+ platform: resolvePlatform()
3903
4037
  });
3904
4038
  let linked = false;
3905
4039
  let linkWarning = "";
@@ -4102,6 +4236,7 @@ function createServer(sdk) {
4102
4236
  scope: z3.string().describe('Scope: "global" or "workspace:<project-name>"'),
4103
4237
  source: z3.string().describe("Source/context of the plan"),
4104
4238
  planFilePath: z3.string().optional().describe("ABSOLUTE path to the local plan file you wrote (e.g. a plan-mode file like /home/user/.claude/plans/<name>.md). REQUIRED whenever you persisted the plan to a file \u2014 always link it so the CogniStore plan points back to the on-disk file."),
4239
+ agentId: z3.string().optional().describe("Your agent/role name (e.g. 'documentation') so plans can be summarized per agent. Pass it whenever you are a named/custom agent."),
4105
4240
  relatedKnowledgeIds: z3.array(z3.string()).optional().describe("IDs of knowledge entries consulted during planning (auto-linked as input)"),
4106
4241
  tasks: z3.array(z3.object({
4107
4242
  description: z3.string(),
@@ -4121,6 +4256,8 @@ function createServer(sdk) {
4121
4256
  scope: params.scope,
4122
4257
  source: params.source,
4123
4258
  planFilePath: params.planFilePath,
4259
+ agentId: normalizeProvenance(params.agentId),
4260
+ platform: resolvePlatform(),
4124
4261
  relatedKnowledgeIds: inputIds.size > 0 ? [...inputIds] : void 0,
4125
4262
  tasks: params.tasks
4126
4263
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cognistore/mcp-server",
3
- "version": "2.2.3",
3
+ "version": "2.3.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "MCP server for CogniStore — integrates with Claude Code and GitHub Copilot",