@cognistore/mcp-server 2.2.2 → 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.
- package/dist/index.js +214 -32
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -15,13 +15,13 @@ import * as sqliteVec from "sqlite-vec";
|
|
|
15
15
|
|
|
16
16
|
// ../../packages/shared/dist/types/knowledge.js
|
|
17
17
|
var KnowledgeType;
|
|
18
|
-
(function(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
(function(KnowledgeType3) {
|
|
19
|
+
KnowledgeType3["DECISION"] = "decision";
|
|
20
|
+
KnowledgeType3["PATTERN"] = "pattern";
|
|
21
|
+
KnowledgeType3["FIX"] = "fix";
|
|
22
|
+
KnowledgeType3["CONSTRAINT"] = "constraint";
|
|
23
|
+
KnowledgeType3["GOTCHA"] = "gotcha";
|
|
24
|
+
KnowledgeType3["SYSTEM"] = "system";
|
|
25
25
|
})(KnowledgeType || (KnowledgeType = {}));
|
|
26
26
|
var KnowledgeStatus;
|
|
27
27
|
(function(KnowledgeStatus2) {
|
|
@@ -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),
|
|
@@ -140,6 +146,25 @@ var mergeTagsBatchSchema = z.object({
|
|
|
140
146
|
to: z.string().min(1, "to is required")
|
|
141
147
|
})).min(1, "at least one merge is required").max(50, "at most 50 merges per batch")
|
|
142
148
|
});
|
|
149
|
+
var MAX_IMPORT_ITEMS = 5e4;
|
|
150
|
+
var importKnowledgeEntrySchema = z.object({
|
|
151
|
+
title: z.string().max(2e3).optional(),
|
|
152
|
+
content: z.string().min(1).max(5e5),
|
|
153
|
+
tags: z.array(z.string().max(200)).max(200).optional().default([]),
|
|
154
|
+
type: knowledgeTypeSchema,
|
|
155
|
+
scope: z.string().min(1).max(300),
|
|
156
|
+
source: z.string().max(300).optional(),
|
|
157
|
+
confidenceScore: z.number().min(0).max(1).optional(),
|
|
158
|
+
expiresAt: z.string().max(64).nullable().optional(),
|
|
159
|
+
relatedIds: z.array(z.string().max(64)).nullable().optional(),
|
|
160
|
+
agentId: z.string().max(200).nullable().optional(),
|
|
161
|
+
platform: z.string().max(200).nullable().optional()
|
|
162
|
+
});
|
|
163
|
+
var importSchema = z.object({
|
|
164
|
+
include: z.array(z.string().max(50)).max(10).optional().default([]),
|
|
165
|
+
knowledge: z.array(importKnowledgeEntrySchema).max(MAX_IMPORT_ITEMS).optional(),
|
|
166
|
+
plans: z.array(z.record(z.unknown())).max(MAX_IMPORT_ITEMS).optional()
|
|
167
|
+
});
|
|
143
168
|
|
|
144
169
|
// ../../packages/core/dist/db/schema/index.js
|
|
145
170
|
var schema_exports = {};
|
|
@@ -174,11 +199,13 @@ var knowledgeEntries = sqliteTable("knowledge_entries", {
|
|
|
174
199
|
confidenceScore: real("confidence_score").notNull().default(1),
|
|
175
200
|
relatedIds: text("related_ids", { mode: "json" }).$type(),
|
|
176
201
|
agentId: text("agent_id"),
|
|
202
|
+
platform: text("platform"),
|
|
177
203
|
createdAt: text("created_at").notNull().$defaultFn(() => (/* @__PURE__ */ new Date()).toISOString()),
|
|
178
204
|
updatedAt: text("updated_at").notNull().$defaultFn(() => (/* @__PURE__ */ new Date()).toISOString())
|
|
179
205
|
}, (table) => [
|
|
180
206
|
index("idx_type").on(table.type),
|
|
181
|
-
index("idx_scope").on(table.scope)
|
|
207
|
+
index("idx_scope").on(table.scope),
|
|
208
|
+
index("idx_knowledge_platform").on(table.platform)
|
|
182
209
|
]);
|
|
183
210
|
|
|
184
211
|
// ../../packages/core/dist/db/schema/sqlite-vec.js
|
|
@@ -419,6 +446,16 @@ ALTER TABLE plans ADD COLUMN plan_file_path TEXT;
|
|
|
419
446
|
`,
|
|
420
447
|
"2.1.0": `
|
|
421
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);
|
|
422
459
|
`
|
|
423
460
|
};
|
|
424
461
|
function runMigrations(sqlite, migrationsDir) {
|
|
@@ -563,7 +600,7 @@ function createPlansEmbeddingsTable(sqlite, dimensions = DEFAULT_EMBEDDING_DIMEN
|
|
|
563
600
|
|
|
564
601
|
// ../../packages/core/dist/repositories/knowledge.repository.js
|
|
565
602
|
import { eq, ne, sql, and, or, isNull } from "drizzle-orm";
|
|
566
|
-
var OPERATIONS_RETENTION_DAYS =
|
|
603
|
+
var OPERATIONS_RETENTION_DAYS = 800;
|
|
567
604
|
var KnowledgeRepository = class _KnowledgeRepository {
|
|
568
605
|
db;
|
|
569
606
|
sqlite;
|
|
@@ -585,8 +622,8 @@ var KnowledgeRepository = class _KnowledgeRepository {
|
|
|
585
622
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
586
623
|
const insertTxn = this.sqlite.transaction(() => {
|
|
587
624
|
this.sqlite.prepare(`INSERT INTO knowledge_entries
|
|
588
|
-
(id, title, content, tags, type, scope, source, version, expires_at, confidence_score, related_ids, agent_id, created_at, updated_at)
|
|
589
|
-
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);
|
|
590
627
|
insertEmbedding(this.sqlite, id, input.embedding);
|
|
591
628
|
try {
|
|
592
629
|
insertFts(this.sqlite, { id, title: input.title, content: input.content, tags: ftsTags(input.tags) });
|
|
@@ -694,6 +731,12 @@ var KnowledgeRepository = class _KnowledgeRepository {
|
|
|
694
731
|
conditions.push(sql`${knowledgeEntries.type} = ${filters.type}`);
|
|
695
732
|
if (filters?.scope)
|
|
696
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
|
+
}
|
|
697
740
|
if (filters?.tags && filters.tags.length > 0) {
|
|
698
741
|
const tagConditions = filters.tags.map((tag) => sql`EXISTS (SELECT 1 FROM json_each(${knowledgeEntries.tags}) WHERE value = ${tag})`);
|
|
699
742
|
conditions.push(or(...tagConditions));
|
|
@@ -944,6 +987,39 @@ var KnowledgeRepository = class _KnowledgeRepository {
|
|
|
944
987
|
}).from(knowledgeEntries).where(and(...conditions)).groupBy(knowledgeEntries.scope);
|
|
945
988
|
return results.map((r) => ({ scope: r.scope, count: Number(r.count) }));
|
|
946
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
|
+
}
|
|
947
1023
|
async listAll() {
|
|
948
1024
|
return this.db.select().from(knowledgeEntries).where(ne(knowledgeEntries.type, "system")).orderBy(sql`${knowledgeEntries.createdAt} DESC`);
|
|
949
1025
|
}
|
|
@@ -958,7 +1034,7 @@ var KnowledgeRepository = class _KnowledgeRepository {
|
|
|
958
1034
|
createPlan(input) {
|
|
959
1035
|
const id = crypto.randomUUID();
|
|
960
1036
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
961
|
-
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);
|
|
962
1038
|
try {
|
|
963
1039
|
insertPlanEmbedding(this.sqlite, id, input.embedding);
|
|
964
1040
|
} catch {
|
|
@@ -1230,6 +1306,28 @@ var KnowledgeRepository = class _KnowledgeRepository {
|
|
|
1230
1306
|
}
|
|
1231
1307
|
return Object.entries(map).map(([date, counts]) => ({ date, ...counts }));
|
|
1232
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
|
+
}
|
|
1233
1331
|
cleanupOldOperations() {
|
|
1234
1332
|
const cutoff = new Date(Date.now() - OPERATIONS_RETENTION_DAYS * 24 * 60 * 60 * 1e3).toISOString();
|
|
1235
1333
|
return this.sqlite.prepare("DELETE FROM operations_log WHERE created_at < ?").run(cutoff).changes;
|
|
@@ -1346,7 +1444,13 @@ var KnowledgeService = class {
|
|
|
1346
1444
|
});
|
|
1347
1445
|
if (similar.length > 0) {
|
|
1348
1446
|
const existing = similar[0].entry;
|
|
1349
|
-
const
|
|
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 });
|
|
1350
1454
|
this.logOp("write");
|
|
1351
1455
|
return { ...this.toKnowledgeEntry(updated), deduplicated: true };
|
|
1352
1456
|
}
|
|
@@ -1731,6 +1835,12 @@ var KnowledgeService = class {
|
|
|
1731
1835
|
async countByScope(opts = {}) {
|
|
1732
1836
|
return this.repository.countByScope(opts);
|
|
1733
1837
|
}
|
|
1838
|
+
async countByAgent(opts = {}) {
|
|
1839
|
+
return this.repository.countByAgent(opts);
|
|
1840
|
+
}
|
|
1841
|
+
async countByPlatform(opts = {}) {
|
|
1842
|
+
return this.repository.countByPlatform(opts);
|
|
1843
|
+
}
|
|
1734
1844
|
// ─── Plans (separate entity) ────────────────────────────────
|
|
1735
1845
|
async createPlan(input) {
|
|
1736
1846
|
const { tasks, skipDedup, ...planInput } = input;
|
|
@@ -1978,6 +2088,9 @@ var KnowledgeService = class {
|
|
|
1978
2088
|
getOperationsByDay(days = 15) {
|
|
1979
2089
|
return this.repository.getOperationsByDay(days);
|
|
1980
2090
|
}
|
|
2091
|
+
getPlansByDay(days = 15) {
|
|
2092
|
+
return this.repository.getPlansByDay(days);
|
|
2093
|
+
}
|
|
1981
2094
|
cleanupOldOperations() {
|
|
1982
2095
|
return this.repository.cleanupOldOperations();
|
|
1983
2096
|
}
|
|
@@ -1999,7 +2112,8 @@ var KnowledgeService = class {
|
|
|
1999
2112
|
expiresAt: row.expiresAt ? new Date(row.expiresAt) : null,
|
|
2000
2113
|
confidenceScore: row.confidenceScore,
|
|
2001
2114
|
relatedIds: row.relatedIds ? Array.isArray(row.relatedIds) ? row.relatedIds : JSON.parse(row.relatedIds) : null,
|
|
2002
|
-
agentId: row.agentId,
|
|
2115
|
+
agentId: row.agentId ?? row.agent_id ?? null,
|
|
2116
|
+
platform: row.platform ?? null,
|
|
2003
2117
|
createdAt: new Date(row.createdAt ?? row.created_at),
|
|
2004
2118
|
updatedAt: new Date(row.updatedAt ?? row.updated_at)
|
|
2005
2119
|
};
|
|
@@ -2014,6 +2128,8 @@ var KnowledgeService = class {
|
|
|
2014
2128
|
status: row.status,
|
|
2015
2129
|
source: row.source ?? "",
|
|
2016
2130
|
planFilePath: row.plan_file_path ?? row.planFilePath ?? null,
|
|
2131
|
+
agentId: row.agent_id ?? row.agentId ?? null,
|
|
2132
|
+
platform: row.platform ?? null,
|
|
2017
2133
|
createdAt: new Date(row.created_at ?? row.createdAt),
|
|
2018
2134
|
updatedAt: new Date(row.updated_at ?? row.updatedAt)
|
|
2019
2135
|
};
|
|
@@ -2538,12 +2654,16 @@ var OllamaEmbeddingClient = class {
|
|
|
2538
2654
|
dimensions;
|
|
2539
2655
|
maxRetries;
|
|
2540
2656
|
maxInputChars;
|
|
2657
|
+
requestTimeoutMs;
|
|
2658
|
+
healthTimeoutMs;
|
|
2541
2659
|
constructor(config) {
|
|
2542
2660
|
this.host = config?.host ?? (process.env.OLLAMA_HOST ?? DEFAULT_OLLAMA_HOST);
|
|
2543
2661
|
this.model = config?.model ?? (process.env.OLLAMA_MODEL ?? DEFAULT_EMBEDDING_MODEL);
|
|
2544
2662
|
this.dimensions = config?.dimensions ?? (Number(process.env.EMBEDDING_DIMENSIONS) || DEFAULT_EMBEDDING_DIMENSIONS);
|
|
2545
2663
|
this.maxRetries = config?.maxRetries ?? 3;
|
|
2546
2664
|
this.maxInputChars = config?.maxInputChars ?? 2e3;
|
|
2665
|
+
this.requestTimeoutMs = config?.requestTimeoutMs ?? 3e4;
|
|
2666
|
+
this.healthTimeoutMs = config?.healthTimeoutMs ?? 1e4;
|
|
2547
2667
|
}
|
|
2548
2668
|
truncateText(text2, maxChars) {
|
|
2549
2669
|
if (text2.length <= maxChars)
|
|
@@ -2590,7 +2710,7 @@ var OllamaEmbeddingClient = class {
|
|
|
2590
2710
|
}
|
|
2591
2711
|
async isHealthy() {
|
|
2592
2712
|
try {
|
|
2593
|
-
const response = await fetch(`${this.host}/api/tags
|
|
2713
|
+
const response = await fetch(`${this.host}/api/tags`, { signal: AbortSignal.timeout(this.healthTimeoutMs) });
|
|
2594
2714
|
return response.ok;
|
|
2595
2715
|
} catch {
|
|
2596
2716
|
return false;
|
|
@@ -2598,7 +2718,7 @@ var OllamaEmbeddingClient = class {
|
|
|
2598
2718
|
}
|
|
2599
2719
|
async isModelAvailable() {
|
|
2600
2720
|
try {
|
|
2601
|
-
const response = await fetch(`${this.host}/api/tags
|
|
2721
|
+
const response = await fetch(`${this.host}/api/tags`, { signal: AbortSignal.timeout(this.healthTimeoutMs) });
|
|
2602
2722
|
if (!response.ok)
|
|
2603
2723
|
return false;
|
|
2604
2724
|
const data = await response.json();
|
|
@@ -2608,20 +2728,43 @@ var OllamaEmbeddingClient = class {
|
|
|
2608
2728
|
}
|
|
2609
2729
|
}
|
|
2610
2730
|
async pullModel() {
|
|
2611
|
-
const
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2731
|
+
const controller = new AbortController();
|
|
2732
|
+
const connectTimer = setTimeout(() => controller.abort(), 3e4);
|
|
2733
|
+
let response;
|
|
2734
|
+
try {
|
|
2735
|
+
response = await fetch(`${this.host}/api/pull`, {
|
|
2736
|
+
method: "POST",
|
|
2737
|
+
headers: { "Content-Type": "application/json" },
|
|
2738
|
+
body: JSON.stringify({ name: this.model }),
|
|
2739
|
+
signal: controller.signal
|
|
2740
|
+
});
|
|
2741
|
+
} finally {
|
|
2742
|
+
clearTimeout(connectTimer);
|
|
2743
|
+
}
|
|
2616
2744
|
if (!response.ok) {
|
|
2617
2745
|
throw new Error(`Failed to pull model ${this.model}: ${response.statusText}`);
|
|
2618
2746
|
}
|
|
2619
2747
|
const reader = response.body?.getReader();
|
|
2620
2748
|
if (reader) {
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2749
|
+
const IDLE_MS = 12e4;
|
|
2750
|
+
try {
|
|
2751
|
+
while (true) {
|
|
2752
|
+
let timer;
|
|
2753
|
+
const idle = new Promise((_, reject) => {
|
|
2754
|
+
timer = setTimeout(() => reject(new Error(`Model pull stalled (no data for ${IDLE_MS / 1e3}s)`)), IDLE_MS);
|
|
2755
|
+
});
|
|
2756
|
+
try {
|
|
2757
|
+
const { done } = await Promise.race([reader.read(), idle]);
|
|
2758
|
+
if (done)
|
|
2759
|
+
break;
|
|
2760
|
+
} finally {
|
|
2761
|
+
clearTimeout(timer);
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
} catch (err) {
|
|
2765
|
+
await reader.cancel().catch(() => {
|
|
2766
|
+
});
|
|
2767
|
+
throw err;
|
|
2625
2768
|
}
|
|
2626
2769
|
}
|
|
2627
2770
|
}
|
|
@@ -2653,7 +2796,7 @@ var OllamaEmbeddingClient = class {
|
|
|
2653
2796
|
let lastError;
|
|
2654
2797
|
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
2655
2798
|
try {
|
|
2656
|
-
return await fetch(url, init);
|
|
2799
|
+
return await fetch(url, { ...init, signal: init.signal ?? AbortSignal.timeout(this.requestTimeoutMs) });
|
|
2657
2800
|
} catch (error) {
|
|
2658
2801
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2659
2802
|
if (attempt < this.maxRetries - 1) {
|
|
@@ -3533,6 +3676,22 @@ var KnowledgeSDK = class {
|
|
|
3533
3676
|
throw this.wrapError(error, "Failed to count by scope");
|
|
3534
3677
|
}
|
|
3535
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
|
+
}
|
|
3536
3695
|
async getStats() {
|
|
3537
3696
|
this.ensureInitialized();
|
|
3538
3697
|
try {
|
|
@@ -3688,6 +3847,10 @@ var KnowledgeSDK = class {
|
|
|
3688
3847
|
this.ensureInitialized();
|
|
3689
3848
|
return this.service.getOperationsByDay(days);
|
|
3690
3849
|
}
|
|
3850
|
+
getPlansByDay(days = 15) {
|
|
3851
|
+
this.ensureInitialized();
|
|
3852
|
+
return this.service.getPlansByDay(days);
|
|
3853
|
+
}
|
|
3691
3854
|
cleanupOldOperations() {
|
|
3692
3855
|
if (!this.initialized || !this.service)
|
|
3693
3856
|
return 0;
|
|
@@ -3834,6 +3997,21 @@ function createServer(sdk) {
|
|
|
3834
3997
|
version: "1.0.0"
|
|
3835
3998
|
});
|
|
3836
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
|
+
}
|
|
3837
4015
|
const knowledgeEntrySchema = z3.object({
|
|
3838
4016
|
title: z3.string().describe("Short descriptive title"),
|
|
3839
4017
|
content: z3.string().describe("The knowledge content text"),
|
|
@@ -3842,7 +4020,7 @@ function createServer(sdk) {
|
|
|
3842
4020
|
scope: z3.string().describe('Scope: "global" or "workspace:<project-name>"'),
|
|
3843
4021
|
source: z3.string().describe("Source of the knowledge"),
|
|
3844
4022
|
confidenceScore: z3.number().min(0).max(1).optional().describe("Confidence score 0-1"),
|
|
3845
|
-
agentId: z3.string().optional().describe("
|
|
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."),
|
|
3846
4024
|
planId: z3.string().optional().describe("Plan ID to auto-link this knowledge as output. ALWAYS pass this if you have an active plan.")
|
|
3847
4025
|
});
|
|
3848
4026
|
async function createEntry(params) {
|
|
@@ -3854,7 +4032,8 @@ function createServer(sdk) {
|
|
|
3854
4032
|
scope: params.scope,
|
|
3855
4033
|
source: params.source,
|
|
3856
4034
|
confidenceScore: params.confidenceScore,
|
|
3857
|
-
agentId: params.agentId
|
|
4035
|
+
agentId: normalizeProvenance(params.agentId),
|
|
4036
|
+
platform: resolvePlatform()
|
|
3858
4037
|
});
|
|
3859
4038
|
let linked = false;
|
|
3860
4039
|
let linkWarning = "";
|
|
@@ -4057,6 +4236,7 @@ function createServer(sdk) {
|
|
|
4057
4236
|
scope: z3.string().describe('Scope: "global" or "workspace:<project-name>"'),
|
|
4058
4237
|
source: z3.string().describe("Source/context of the plan"),
|
|
4059
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."),
|
|
4060
4240
|
relatedKnowledgeIds: z3.array(z3.string()).optional().describe("IDs of knowledge entries consulted during planning (auto-linked as input)"),
|
|
4061
4241
|
tasks: z3.array(z3.object({
|
|
4062
4242
|
description: z3.string(),
|
|
@@ -4076,6 +4256,8 @@ function createServer(sdk) {
|
|
|
4076
4256
|
scope: params.scope,
|
|
4077
4257
|
source: params.source,
|
|
4078
4258
|
planFilePath: params.planFilePath,
|
|
4259
|
+
agentId: normalizeProvenance(params.agentId),
|
|
4260
|
+
platform: resolvePlatform(),
|
|
4079
4261
|
relatedKnowledgeIds: inputIds.size > 0 ? [...inputIds] : void 0,
|
|
4080
4262
|
tasks: params.tasks
|
|
4081
4263
|
});
|
|
@@ -4232,7 +4414,7 @@ function createServer(sdk) {
|
|
|
4232
4414
|
},
|
|
4233
4415
|
WRITE,
|
|
4234
4416
|
async (params) => {
|
|
4235
|
-
const result = sdk.updatePlan(params.planId, { status:
|
|
4417
|
+
const result = sdk.updatePlan(params.planId, { status: KnowledgeStatus.ARCHIVED });
|
|
4236
4418
|
if (!result) return { content: [{ type: "text", text: JSON.stringify({ error: "not_found", type: "plan", id: params.planId }) }] };
|
|
4237
4419
|
return { content: [{ type: "text", text: JSON.stringify({ archived: true, id: result.id, status: result.status }, null, 2) }] };
|
|
4238
4420
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cognistore/mcp-server",
|
|
3
|
-
"version": "2.
|
|
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",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"build:deps": "pnpm --filter @cognistore/shared build && pnpm --filter @cognistore/core build && pnpm --filter @cognistore/embeddings build && pnpm --filter @cognistore/sdk build",
|
|
36
36
|
"dev": "tsx src/index.ts",
|
|
37
37
|
"clean": "rm -rf dist",
|
|
38
|
-
"test": "if find . -name '*.test.ts' -o -name '*.spec.ts' | grep -q .; then npx playwright test; else echo 'No tests found, skipping'; fi"
|
|
38
|
+
"test": "if find . -name '*.test.ts' -o -name '*.spec.ts' | grep -q .; then npx playwright test; else echo 'No tests found, skipping'; fi",
|
|
39
|
+
"type-check": "tsc --noEmit"
|
|
39
40
|
}
|
|
40
41
|
}
|