@vheins/local-memory-mcp 0.13.1 → 0.14.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/README.id.md +94 -0
- package/README.md +19 -24
- package/dist/{chunk-UZWV2DYU.js → chunk-GXQCHTXK.js} +623 -76
- package/dist/dashboard/public/assets/index-C1OTQhII.js +87 -0
- package/dist/dashboard/public/assets/index-CUg8rZCA.css +1 -0
- package/dist/dashboard/public/index.html +2 -2
- package/dist/dashboard/server.js +37 -16
- package/dist/mcp/server.js +197 -424
- package/package.json +3 -2
- package/dist/dashboard/public/assets/index-CbVS6V5d.js +0 -87
- package/dist/dashboard/public/assets/index-d4GwN8c5.css +0 -1
|
@@ -81,8 +81,8 @@ function loadServerInstructions() {
|
|
|
81
81
|
// src/mcp/capabilities.ts
|
|
82
82
|
var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
|
|
83
83
|
var pkgVersion = "0.1.0";
|
|
84
|
-
if ("0.
|
|
85
|
-
pkgVersion = "0.
|
|
84
|
+
if ("0.14.0") {
|
|
85
|
+
pkgVersion = "0.14.0";
|
|
86
86
|
} else {
|
|
87
87
|
let searchDir = __dirname2;
|
|
88
88
|
for (let i = 0; i < 5; i++) {
|
|
@@ -1119,6 +1119,23 @@ var BaseEntity = class {
|
|
|
1119
1119
|
};
|
|
1120
1120
|
|
|
1121
1121
|
// src/mcp/entities/memory.ts
|
|
1122
|
+
var VALID_COLUMNS = /* @__PURE__ */ new Set([
|
|
1123
|
+
"code",
|
|
1124
|
+
"type",
|
|
1125
|
+
"title",
|
|
1126
|
+
"content",
|
|
1127
|
+
"importance",
|
|
1128
|
+
"agent",
|
|
1129
|
+
"role",
|
|
1130
|
+
"model",
|
|
1131
|
+
"completed_at",
|
|
1132
|
+
"expires_at",
|
|
1133
|
+
"supersedes",
|
|
1134
|
+
"status",
|
|
1135
|
+
"hit_count",
|
|
1136
|
+
"recall_count",
|
|
1137
|
+
"last_used_at"
|
|
1138
|
+
]);
|
|
1122
1139
|
var MemoryEntity = class extends BaseEntity {
|
|
1123
1140
|
insert(entry) {
|
|
1124
1141
|
this.run(
|
|
@@ -1179,7 +1196,7 @@ var MemoryEntity = class extends BaseEntity {
|
|
|
1179
1196
|
} else if (k === "is_global") {
|
|
1180
1197
|
fields.push(`${k} = ?`);
|
|
1181
1198
|
values.push(val ? 1 : 0);
|
|
1182
|
-
} else if (k
|
|
1199
|
+
} else if (VALID_COLUMNS.has(k)) {
|
|
1183
1200
|
fields.push(`${k} = ?`);
|
|
1184
1201
|
values.push(val);
|
|
1185
1202
|
}
|
|
@@ -1303,13 +1320,27 @@ var MemoryEntity = class extends BaseEntity {
|
|
|
1303
1320
|
Object.keys(updates).forEach((key) => {
|
|
1304
1321
|
const value = updates[key];
|
|
1305
1322
|
if (value !== void 0) {
|
|
1306
|
-
if (key === "
|
|
1323
|
+
if (key === "scope") {
|
|
1324
|
+
const scope = updates.scope;
|
|
1325
|
+
if (scope?.repo) {
|
|
1326
|
+
fields.push("repo = ?");
|
|
1327
|
+
values.push(scope.repo);
|
|
1328
|
+
}
|
|
1329
|
+
if (scope?.folder !== void 0) {
|
|
1330
|
+
fields.push("folder = ?");
|
|
1331
|
+
values.push(scope.folder);
|
|
1332
|
+
}
|
|
1333
|
+
if (scope?.language !== void 0) {
|
|
1334
|
+
fields.push("language = ?");
|
|
1335
|
+
values.push(scope.language);
|
|
1336
|
+
}
|
|
1337
|
+
} else if (key === "tags" || key === "metadata") {
|
|
1307
1338
|
fields.push(`${key} = ?`);
|
|
1308
1339
|
values.push(JSON.stringify(value));
|
|
1309
1340
|
} else if (key === "is_global") {
|
|
1310
1341
|
fields.push(`${key} = ?`);
|
|
1311
1342
|
values.push(value ? 1 : 0);
|
|
1312
|
-
} else {
|
|
1343
|
+
} else if (VALID_COLUMNS.has(key)) {
|
|
1313
1344
|
fields.push(`${key} = ?`);
|
|
1314
1345
|
values.push(value);
|
|
1315
1346
|
}
|
|
@@ -1332,19 +1363,6 @@ var MemoryEntity = class extends BaseEntity {
|
|
|
1332
1363
|
return count;
|
|
1333
1364
|
});
|
|
1334
1365
|
}
|
|
1335
|
-
bulkDeleteMemories(ids) {
|
|
1336
|
-
if (ids.length === 0) return 0;
|
|
1337
|
-
return this.transaction(() => {
|
|
1338
|
-
let count = 0;
|
|
1339
|
-
const chunkSize = 500;
|
|
1340
|
-
for (let i = 0; i < ids.length; i += chunkSize) {
|
|
1341
|
-
const chunk = ids.slice(i, i + chunkSize);
|
|
1342
|
-
const result = this.run(`DELETE FROM memories WHERE id IN (${chunk.map(() => "?").join(",")})`, chunk);
|
|
1343
|
-
count += result.changes;
|
|
1344
|
-
}
|
|
1345
|
-
return count;
|
|
1346
|
-
});
|
|
1347
|
-
}
|
|
1348
1366
|
getRecentMemories(repo, limit, offset = 0, includeArchived = false, excludeTypes = [], sortOrder = "DESC") {
|
|
1349
1367
|
let query = "SELECT * FROM memories WHERE repo = ?";
|
|
1350
1368
|
const params = [repo];
|
|
@@ -1390,24 +1408,6 @@ var MemoryEntity = class extends BaseEntity {
|
|
|
1390
1408
|
id
|
|
1391
1409
|
]);
|
|
1392
1410
|
}
|
|
1393
|
-
getVectorCandidates(repo, limit = 100) {
|
|
1394
|
-
let sql = `SELECT mv.memory_id, mv.vector FROM memory_vectors mv JOIN memories m ON mv.memory_id = m.id`;
|
|
1395
|
-
const params = [];
|
|
1396
|
-
if (repo) {
|
|
1397
|
-
sql += " WHERE m.repo = ?";
|
|
1398
|
-
params.push(repo);
|
|
1399
|
-
}
|
|
1400
|
-
sql += " LIMIT ?";
|
|
1401
|
-
params.push(limit);
|
|
1402
|
-
return this.all(sql, params);
|
|
1403
|
-
}
|
|
1404
|
-
upsertVectorEmbedding(memoryId, vector) {
|
|
1405
|
-
this.run(
|
|
1406
|
-
`INSERT INTO memory_vectors (memory_id, vector, updated_at) VALUES (?, ?, ?)
|
|
1407
|
-
ON CONFLICT(memory_id) DO UPDATE SET vector = excluded.vector, updated_at = excluded.updated_at`,
|
|
1408
|
-
[memoryId, JSON.stringify(vector), (/* @__PURE__ */ new Date()).toISOString()]
|
|
1409
|
-
);
|
|
1410
|
-
}
|
|
1411
1411
|
getSummary(repo) {
|
|
1412
1412
|
const row = this.get(
|
|
1413
1413
|
"SELECT summary, updated_at FROM memory_summary WHERE repo = ?",
|
|
@@ -1487,6 +1487,28 @@ var MemoryEntity = class extends BaseEntity {
|
|
|
1487
1487
|
}));
|
|
1488
1488
|
return { items, memories: items, total, limit, offset };
|
|
1489
1489
|
}
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
// src/mcp/entities/memory.vector.ts
|
|
1493
|
+
var MemoryVectorEntity = class extends BaseEntity {
|
|
1494
|
+
getVectorCandidates(repo, limit = 100) {
|
|
1495
|
+
let sql = `SELECT mv.memory_id, mv.vector FROM memory_vectors mv JOIN memories m ON mv.memory_id = m.id`;
|
|
1496
|
+
const params = [];
|
|
1497
|
+
if (repo) {
|
|
1498
|
+
sql += " WHERE m.repo = ?";
|
|
1499
|
+
params.push(repo);
|
|
1500
|
+
}
|
|
1501
|
+
sql += " LIMIT ?";
|
|
1502
|
+
params.push(limit);
|
|
1503
|
+
return this.all(sql, params);
|
|
1504
|
+
}
|
|
1505
|
+
upsertVectorEmbedding(memoryId, vector) {
|
|
1506
|
+
this.run(
|
|
1507
|
+
`INSERT INTO memory_vectors (memory_id, vector, updated_at) VALUES (?, ?, ?)
|
|
1508
|
+
ON CONFLICT(memory_id) DO UPDATE SET vector = excluded.vector, updated_at = excluded.updated_at`,
|
|
1509
|
+
[memoryId, JSON.stringify(vector), (/* @__PURE__ */ new Date()).toISOString()]
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1490
1512
|
searchBySimilarity(query, repo, limit = 10, includeArchived = false, currentTags = []) {
|
|
1491
1513
|
const queryVector = this.computeVector(query);
|
|
1492
1514
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -1504,8 +1526,12 @@ var MemoryEntity = class extends BaseEntity {
|
|
|
1504
1526
|
if (candidates.length < 5) {
|
|
1505
1527
|
const recentSql = `SELECT * FROM memories WHERE (${where.join(" OR ")}) AND status = 'active' AND (expires_at IS NULL OR expires_at > ?) ORDER BY created_at DESC LIMIT 10`;
|
|
1506
1528
|
const recent = this.all(recentSql, [...params, now.toISOString()]);
|
|
1529
|
+
const candidateIds = new Set(candidates.map((c) => c.id));
|
|
1507
1530
|
for (const r of recent) {
|
|
1508
|
-
if (!
|
|
1531
|
+
if (!candidateIds.has(r.id)) {
|
|
1532
|
+
candidateIds.add(r.id);
|
|
1533
|
+
candidates.push(r);
|
|
1534
|
+
}
|
|
1509
1535
|
}
|
|
1510
1536
|
}
|
|
1511
1537
|
return candidates.map((row) => {
|
|
@@ -1531,6 +1557,23 @@ var MemoryEntity = class extends BaseEntity {
|
|
|
1531
1557
|
}
|
|
1532
1558
|
return null;
|
|
1533
1559
|
}
|
|
1560
|
+
};
|
|
1561
|
+
|
|
1562
|
+
// src/mcp/entities/memory.archive.ts
|
|
1563
|
+
var MemoryArchiveEntity = class extends BaseEntity {
|
|
1564
|
+
bulkDeleteMemories(ids) {
|
|
1565
|
+
if (ids.length === 0) return 0;
|
|
1566
|
+
return this.transaction(() => {
|
|
1567
|
+
let count = 0;
|
|
1568
|
+
const chunkSize = 500;
|
|
1569
|
+
for (let i = 0; i < ids.length; i += chunkSize) {
|
|
1570
|
+
const chunk = ids.slice(i, i + chunkSize);
|
|
1571
|
+
const result = this.run(`DELETE FROM memories WHERE id IN (${chunk.map(() => "?").join(",")})`, chunk);
|
|
1572
|
+
count += result.changes;
|
|
1573
|
+
}
|
|
1574
|
+
return count;
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1534
1577
|
archiveExpiredMemories(force = false) {
|
|
1535
1578
|
if (process.env.ENABLE_AUTO_ARCHIVE !== "true" && !force) return 0;
|
|
1536
1579
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1606,7 +1649,7 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1606
1649
|
const fields = [];
|
|
1607
1650
|
const values = [];
|
|
1608
1651
|
const anyUpdates = updates;
|
|
1609
|
-
const
|
|
1652
|
+
const VALID_COLUMNS2 = /* @__PURE__ */ new Set([
|
|
1610
1653
|
"repo",
|
|
1611
1654
|
"task_code",
|
|
1612
1655
|
"phase",
|
|
@@ -1629,7 +1672,7 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1629
1672
|
"changed_files"
|
|
1630
1673
|
]);
|
|
1631
1674
|
Object.keys(updates).forEach((key) => {
|
|
1632
|
-
if (
|
|
1675
|
+
if (VALID_COLUMNS2.has(key) && anyUpdates[key] !== void 0) {
|
|
1633
1676
|
if (key === "tags" || key === "metadata" || key === "changed_files") {
|
|
1634
1677
|
fields.push(`${key} = ?`);
|
|
1635
1678
|
values.push(JSON.stringify(anyUpdates[key]));
|
|
@@ -1659,7 +1702,7 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1659
1702
|
WHERE t.id = ?`,
|
|
1660
1703
|
[id]
|
|
1661
1704
|
);
|
|
1662
|
-
return row ? { ...this.rowToTask(row), comments: this.
|
|
1705
|
+
return row ? { ...this.rowToTask(row), comments: this.all("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at DESC, id DESC", [id]) } : null;
|
|
1663
1706
|
}
|
|
1664
1707
|
getTasksByIds(ids) {
|
|
1665
1708
|
if (ids.length === 0) return [];
|
|
@@ -1700,7 +1743,7 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1700
1743
|
WHERE t.repo = ? AND t.task_code = ?`,
|
|
1701
1744
|
[repo, taskCode]
|
|
1702
1745
|
);
|
|
1703
|
-
return row ? { ...this.rowToTask(row), comments: this.
|
|
1746
|
+
return row ? { ...this.rowToTask(row), comments: this.all("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at DESC, id DESC", [row.id]) } : null;
|
|
1704
1747
|
}
|
|
1705
1748
|
getTasksByRepo(repo, status, limit, offset, search) {
|
|
1706
1749
|
let query = `
|
|
@@ -1886,6 +1929,10 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1886
1929
|
return count;
|
|
1887
1930
|
});
|
|
1888
1931
|
}
|
|
1932
|
+
};
|
|
1933
|
+
|
|
1934
|
+
// src/mcp/entities/task-comment.ts
|
|
1935
|
+
var TaskCommentEntity = class extends BaseEntity {
|
|
1889
1936
|
insertTaskComment(comment) {
|
|
1890
1937
|
this.run(
|
|
1891
1938
|
`INSERT INTO task_comments (
|
|
@@ -1909,8 +1956,17 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1909
1956
|
const fields = [];
|
|
1910
1957
|
const values = [];
|
|
1911
1958
|
const anyUpdates = updates;
|
|
1959
|
+
const VALID_COLUMNS2 = /* @__PURE__ */ new Set([
|
|
1960
|
+
"repo",
|
|
1961
|
+
"comment",
|
|
1962
|
+
"agent",
|
|
1963
|
+
"role",
|
|
1964
|
+
"model",
|
|
1965
|
+
"previous_status",
|
|
1966
|
+
"next_status"
|
|
1967
|
+
]);
|
|
1912
1968
|
Object.keys(updates).forEach((key) => {
|
|
1913
|
-
if (anyUpdates[key] !== void 0) {
|
|
1969
|
+
if (VALID_COLUMNS2.has(key) && anyUpdates[key] !== void 0) {
|
|
1914
1970
|
fields.push(`${key} = ?`);
|
|
1915
1971
|
values.push(anyUpdates[key]);
|
|
1916
1972
|
}
|
|
@@ -1935,6 +1991,10 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1935
1991
|
repo
|
|
1936
1992
|
]);
|
|
1937
1993
|
}
|
|
1994
|
+
};
|
|
1995
|
+
|
|
1996
|
+
// src/mcp/entities/task-stats.ts
|
|
1997
|
+
var TaskStatsEntity = class extends BaseEntity {
|
|
1938
1998
|
getTaskStats(repo) {
|
|
1939
1999
|
const rows = this.all(
|
|
1940
2000
|
"SELECT status, COUNT(*) as count FROM tasks WHERE repo = ? GROUP BY status",
|
|
@@ -2863,7 +2923,11 @@ var DB_PATH = resolveDbPath();
|
|
|
2863
2923
|
var SQLiteStore = class _SQLiteStore {
|
|
2864
2924
|
db;
|
|
2865
2925
|
memories;
|
|
2926
|
+
memoryVectors;
|
|
2927
|
+
memoryArchives;
|
|
2866
2928
|
tasks;
|
|
2929
|
+
taskComments;
|
|
2930
|
+
taskStats;
|
|
2867
2931
|
actions;
|
|
2868
2932
|
system;
|
|
2869
2933
|
summaries;
|
|
@@ -2894,7 +2958,11 @@ var SQLiteStore = class _SQLiteStore {
|
|
|
2894
2958
|
migrator.addMemoryCodeColumn();
|
|
2895
2959
|
migrator.addStandardCodeColumn();
|
|
2896
2960
|
this.memories = new MemoryEntity(this.db);
|
|
2961
|
+
this.memoryVectors = new MemoryVectorEntity(this.db);
|
|
2962
|
+
this.memoryArchives = new MemoryArchiveEntity(this.db);
|
|
2897
2963
|
this.tasks = new TaskEntity(this.db);
|
|
2964
|
+
this.taskComments = new TaskCommentEntity(this.db);
|
|
2965
|
+
this.taskStats = new TaskStatsEntity(this.db);
|
|
2898
2966
|
this.actions = new ActionEntity(this.db);
|
|
2899
2967
|
this.system = new SystemEntity(this.db);
|
|
2900
2968
|
this.summaries = new SummaryEntity(this.db);
|
|
@@ -3045,7 +3113,7 @@ var RealVectorStore = class {
|
|
|
3045
3113
|
if (kind === "standard") {
|
|
3046
3114
|
this.db.standards.upsertVectorEmbedding(id, vector);
|
|
3047
3115
|
} else {
|
|
3048
|
-
this.db.
|
|
3116
|
+
this.db.memoryVectors.upsertVectorEmbedding(id, vector);
|
|
3049
3117
|
}
|
|
3050
3118
|
} catch (error) {
|
|
3051
3119
|
logger.error("[Vectors] Error during upsert", { id, kind, error: String(error) });
|
|
@@ -3061,7 +3129,7 @@ var RealVectorStore = class {
|
|
|
3061
3129
|
const extractor = await this.getExtractor();
|
|
3062
3130
|
const output = await extractor(query, { pooling: "mean", normalize: true });
|
|
3063
3131
|
const queryVector = Array.from(output.data);
|
|
3064
|
-
const rows = kind === "standard" ? this.db.standards.getVectorCandidates(repo, 100).map((row) => ({ id: row.standard_id, vector: row.vector })) : this.db.
|
|
3132
|
+
const rows = kind === "standard" ? this.db.standards.getVectorCandidates(repo, 100).map((row) => ({ id: row.standard_id, vector: row.vector })) : this.db.memoryVectors.getVectorCandidates(repo, 100).map((row) => ({ id: row.memory_id, vector: row.vector }));
|
|
3065
3133
|
const results = rows.map((row) => {
|
|
3066
3134
|
const memoryVector = JSON.parse(row.vector);
|
|
3067
3135
|
return {
|
|
@@ -3206,7 +3274,8 @@ var MemoryStoreSchema = z.object({
|
|
|
3206
3274
|
structured: z.boolean().default(false)
|
|
3207
3275
|
});
|
|
3208
3276
|
var MemoryUpdateSchema = z.object({
|
|
3209
|
-
id: z.string().uuid(),
|
|
3277
|
+
id: z.string().uuid().optional(),
|
|
3278
|
+
code: z.string().max(20).optional(),
|
|
3210
3279
|
type: MemoryTypeSchema.optional(),
|
|
3211
3280
|
title: z.string().min(3).max(255).optional(),
|
|
3212
3281
|
content: z.string().min(10).optional(),
|
|
@@ -3220,6 +3289,8 @@ var MemoryUpdateSchema = z.object({
|
|
|
3220
3289
|
is_global: z.boolean().optional(),
|
|
3221
3290
|
completed_at: z.string().optional(),
|
|
3222
3291
|
structured: z.boolean().default(false)
|
|
3292
|
+
}).refine((data) => data.id !== void 0 || data.code !== void 0, {
|
|
3293
|
+
message: "Either id or code must be provided"
|
|
3223
3294
|
}).refine(
|
|
3224
3295
|
(data) => data.type !== void 0 || data.content !== void 0 || data.title !== void 0 || data.importance !== void 0 || data.status !== void 0 || data.supersedes !== void 0 || data.tags !== void 0 || data.metadata !== void 0 || data.is_global !== void 0 || data.agent !== void 0 || data.role !== void 0 || data.completed_at !== void 0,
|
|
3225
3296
|
{ message: "At least one field must be provided for update" }
|
|
@@ -3240,10 +3311,13 @@ var MemorySearchSchema = z.object({
|
|
|
3240
3311
|
structured: z.boolean().default(false)
|
|
3241
3312
|
});
|
|
3242
3313
|
var MemoryAcknowledgeSchema = z.object({
|
|
3243
|
-
memory_id: z.string().uuid(),
|
|
3314
|
+
memory_id: z.string().uuid().optional(),
|
|
3315
|
+
code: z.string().max(20).optional(),
|
|
3244
3316
|
status: z.enum(["used", "irrelevant", "contradictory"]),
|
|
3245
3317
|
application_context: z.string().min(10).optional(),
|
|
3246
3318
|
structured: z.boolean().default(false)
|
|
3319
|
+
}).refine((data) => data.memory_id !== void 0 || data.code !== void 0, {
|
|
3320
|
+
message: "Either memory_id or code must be provided"
|
|
3247
3321
|
});
|
|
3248
3322
|
var MemoryRecapSchema = z.object({
|
|
3249
3323
|
repo: z.string().min(1).transform(normalizeRepo),
|
|
@@ -3255,10 +3329,15 @@ var MemoryDeleteSchema = z.object({
|
|
|
3255
3329
|
repo: z.string().min(1).transform(normalizeRepo).optional(),
|
|
3256
3330
|
id: z.string().uuid().optional(),
|
|
3257
3331
|
ids: z.array(z.string().uuid()).min(1).optional(),
|
|
3332
|
+
code: z.string().max(20).optional(),
|
|
3333
|
+
codes: z.array(z.string().max(20)).min(1).optional(),
|
|
3258
3334
|
structured: z.boolean().default(false)
|
|
3259
|
-
}).refine(
|
|
3260
|
-
|
|
3261
|
-
|
|
3335
|
+
}).refine(
|
|
3336
|
+
(data) => data.id !== void 0 || data.ids !== void 0 || data.code !== void 0 || data.codes !== void 0,
|
|
3337
|
+
{
|
|
3338
|
+
message: "Either 'id', 'ids', 'code', or 'codes' must be provided for deletion"
|
|
3339
|
+
}
|
|
3340
|
+
);
|
|
3262
3341
|
var MemorySummarizeSchema = z.object({
|
|
3263
3342
|
repo: z.string().min(1).transform(normalizeRepo),
|
|
3264
3343
|
signals: z.array(z.string().max(200)).min(1),
|
|
@@ -3366,6 +3445,8 @@ var TaskSearchSchema = z.object({
|
|
|
3366
3445
|
repo: z.string().min(1).transform(normalizeRepo),
|
|
3367
3446
|
query: z.string().min(1),
|
|
3368
3447
|
status: z.string().optional(),
|
|
3448
|
+
phase: z.string().optional(),
|
|
3449
|
+
priority: z.number().min(1).max(5).optional(),
|
|
3369
3450
|
limit: z.number().min(1).max(100).default(10),
|
|
3370
3451
|
offset: z.number().min(0).default(0),
|
|
3371
3452
|
structured: z.boolean().default(false)
|
|
@@ -3374,9 +3455,10 @@ var TaskDeleteSchema = z.object({
|
|
|
3374
3455
|
repo: z.string().min(1).transform(normalizeRepo),
|
|
3375
3456
|
id: z.string().uuid().optional(),
|
|
3376
3457
|
ids: z.array(z.string().uuid()).min(1).optional(),
|
|
3458
|
+
task_code: z.string().optional(),
|
|
3377
3459
|
structured: z.boolean().default(false)
|
|
3378
|
-
}).refine((data) => data.id !== void 0 || data.ids !== void 0, {
|
|
3379
|
-
message: "Either 'id' or '
|
|
3460
|
+
}).refine((data) => data.id !== void 0 || data.ids !== void 0 || data.task_code !== void 0, {
|
|
3461
|
+
message: "Either 'id', 'ids', or 'task_code' must be provided for deletion"
|
|
3380
3462
|
});
|
|
3381
3463
|
var MemoryDetailSchema = z.object({
|
|
3382
3464
|
id: z.string().uuid().optional(),
|
|
@@ -3396,10 +3478,15 @@ var StandardDeleteSchema = z.object({
|
|
|
3396
3478
|
repo: z.string().min(1).transform(normalizeRepo).optional(),
|
|
3397
3479
|
id: z.string().uuid().optional(),
|
|
3398
3480
|
ids: z.array(z.string().uuid()).min(1).optional(),
|
|
3481
|
+
code: z.string().max(20).optional(),
|
|
3482
|
+
codes: z.array(z.string().max(20)).min(1).optional(),
|
|
3399
3483
|
structured: z.boolean().default(false)
|
|
3400
|
-
}).refine(
|
|
3401
|
-
|
|
3402
|
-
|
|
3484
|
+
}).refine(
|
|
3485
|
+
(data) => data.id !== void 0 || data.ids !== void 0 || data.code !== void 0 || data.codes !== void 0,
|
|
3486
|
+
{
|
|
3487
|
+
message: "Either 'id', 'ids', 'code', or 'codes' must be provided for deletion"
|
|
3488
|
+
}
|
|
3489
|
+
);
|
|
3403
3490
|
var TaskGetSchema = z.object({
|
|
3404
3491
|
repo: z.string().min(1).transform(normalizeRepo),
|
|
3405
3492
|
id: z.string().uuid().optional(),
|
|
@@ -3494,7 +3581,8 @@ var StandardStoreSchema = z.object({
|
|
|
3494
3581
|
message: "repo is required for repo-specific standards"
|
|
3495
3582
|
});
|
|
3496
3583
|
var StandardUpdateSchema = z.object({
|
|
3497
|
-
id: z.string().uuid(),
|
|
3584
|
+
id: z.string().uuid().optional(),
|
|
3585
|
+
code: z.string().max(20).optional(),
|
|
3498
3586
|
name: z.string().min(3).max(255).optional(),
|
|
3499
3587
|
content: z.string().min(10).optional(),
|
|
3500
3588
|
parent_id: z.string().uuid().nullable().optional(),
|
|
@@ -3509,6 +3597,8 @@ var StandardUpdateSchema = z.object({
|
|
|
3509
3597
|
agent: z.string().optional(),
|
|
3510
3598
|
model: z.string().optional(),
|
|
3511
3599
|
structured: z.boolean().default(false)
|
|
3600
|
+
}).refine((data) => data.id !== void 0 || data.code !== void 0, {
|
|
3601
|
+
message: "Either id or code must be provided"
|
|
3512
3602
|
}).refine(
|
|
3513
3603
|
(data) => data.name !== void 0 || data.content !== void 0 || data.parent_id !== void 0 || data.context !== void 0 || data.version !== void 0 || data.language !== void 0 || data.stack !== void 0 || data.repo !== void 0 || data.is_global !== void 0 || data.tags !== void 0 || data.metadata !== void 0 || data.agent !== void 0 || data.model !== void 0,
|
|
3514
3604
|
{ message: "At least one field must be provided for update" }
|
|
@@ -3781,12 +3871,13 @@ var TOOL_DEFINITIONS = [
|
|
|
3781
3871
|
inputSchema: {
|
|
3782
3872
|
type: "object",
|
|
3783
3873
|
properties: {
|
|
3784
|
-
memory_id: { type: "string", format: "uuid" },
|
|
3874
|
+
memory_id: { type: "string", format: "uuid", description: "Memory entry ID. Optional if code is provided." },
|
|
3875
|
+
code: { type: "string", maxLength: 20, description: "Short memory code. Optional if memory_id is provided." },
|
|
3785
3876
|
status: { type: "string", enum: ["used", "irrelevant", "contradictory"] },
|
|
3786
3877
|
application_context: { type: "string", minLength: 10 },
|
|
3787
3878
|
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
3788
3879
|
},
|
|
3789
|
-
required: ["
|
|
3880
|
+
required: ["status"]
|
|
3790
3881
|
},
|
|
3791
3882
|
outputSchema: {
|
|
3792
3883
|
type: "object",
|
|
@@ -3811,7 +3902,8 @@ var TOOL_DEFINITIONS = [
|
|
|
3811
3902
|
inputSchema: {
|
|
3812
3903
|
type: "object",
|
|
3813
3904
|
properties: {
|
|
3814
|
-
id: { type: "string", format: "uuid" },
|
|
3905
|
+
id: { type: "string", format: "uuid", description: "Memory entry ID. Optional if code is provided." },
|
|
3906
|
+
code: { type: "string", maxLength: 20, description: "Short memory code. Optional if id is provided." },
|
|
3815
3907
|
type: {
|
|
3816
3908
|
type: "string",
|
|
3817
3909
|
enum: ["code_fact", "decision", "mistake", "pattern", "task_archive"]
|
|
@@ -3832,21 +3924,21 @@ var TOOL_DEFINITIONS = [
|
|
|
3832
3924
|
default: false,
|
|
3833
3925
|
description: "If true, returns structured JSON of the updated memory."
|
|
3834
3926
|
}
|
|
3835
|
-
}
|
|
3836
|
-
required: ["id"]
|
|
3927
|
+
}
|
|
3837
3928
|
},
|
|
3838
3929
|
outputSchema: {
|
|
3839
3930
|
type: "object",
|
|
3840
3931
|
properties: {
|
|
3841
3932
|
success: { type: "boolean" },
|
|
3842
3933
|
id: { type: "string" },
|
|
3934
|
+
code: { type: "string" },
|
|
3843
3935
|
repo: { type: "string" },
|
|
3844
3936
|
updatedFields: {
|
|
3845
3937
|
type: "array",
|
|
3846
3938
|
items: { type: "string" }
|
|
3847
3939
|
}
|
|
3848
3940
|
},
|
|
3849
|
-
required: ["success", "
|
|
3941
|
+
required: ["success", "repo", "updatedFields"]
|
|
3850
3942
|
}
|
|
3851
3943
|
},
|
|
3852
3944
|
{
|
|
@@ -3976,13 +4068,20 @@ var TOOL_DEFINITIONS = [
|
|
|
3976
4068
|
type: "object",
|
|
3977
4069
|
properties: {
|
|
3978
4070
|
repo: { type: "string", description: "Repository name (optional for single id)" },
|
|
3979
|
-
id: { type: "string", format: "uuid", description: "Memory entry ID to delete" },
|
|
4071
|
+
id: { type: "string", format: "uuid", description: "Memory entry ID to delete. Optional if code is provided." },
|
|
3980
4072
|
ids: {
|
|
3981
4073
|
type: "array",
|
|
3982
4074
|
items: { type: "string", format: "uuid" },
|
|
3983
4075
|
minItems: 1,
|
|
3984
4076
|
description: "Array of memory IDs to delete"
|
|
3985
4077
|
},
|
|
4078
|
+
code: { type: "string", maxLength: 20, description: "Short memory code. Optional if id is provided." },
|
|
4079
|
+
codes: {
|
|
4080
|
+
type: "array",
|
|
4081
|
+
items: { type: "string", maxLength: 20 },
|
|
4082
|
+
minItems: 1,
|
|
4083
|
+
description: "Array of memory codes to delete"
|
|
4084
|
+
},
|
|
3986
4085
|
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
3987
4086
|
}
|
|
3988
4087
|
},
|
|
@@ -3991,7 +4090,9 @@ var TOOL_DEFINITIONS = [
|
|
|
3991
4090
|
properties: {
|
|
3992
4091
|
success: { type: "boolean" },
|
|
3993
4092
|
id: { type: "string" },
|
|
4093
|
+
code: { type: "string" },
|
|
3994
4094
|
ids: { type: "array", items: { type: "string" } },
|
|
4095
|
+
codes: { type: "array", items: { type: "string" } },
|
|
3995
4096
|
repo: { type: "string" },
|
|
3996
4097
|
deletedCount: { type: "number" }
|
|
3997
4098
|
},
|
|
@@ -4012,13 +4113,24 @@ var TOOL_DEFINITIONS = [
|
|
|
4012
4113
|
type: "object",
|
|
4013
4114
|
properties: {
|
|
4014
4115
|
repo: { type: "string", description: "Repository name (optional for single id)" },
|
|
4015
|
-
id: {
|
|
4116
|
+
id: {
|
|
4117
|
+
type: "string",
|
|
4118
|
+
format: "uuid",
|
|
4119
|
+
description: "Coding standard ID to delete. Optional if code is provided."
|
|
4120
|
+
},
|
|
4016
4121
|
ids: {
|
|
4017
4122
|
type: "array",
|
|
4018
4123
|
items: { type: "string", format: "uuid" },
|
|
4019
4124
|
minItems: 1,
|
|
4020
4125
|
description: "Array of coding standard IDs to delete"
|
|
4021
4126
|
},
|
|
4127
|
+
code: { type: "string", maxLength: 20, description: "Short standard code. Optional if id is provided." },
|
|
4128
|
+
codes: {
|
|
4129
|
+
type: "array",
|
|
4130
|
+
items: { type: "string", maxLength: 20 },
|
|
4131
|
+
minItems: 1,
|
|
4132
|
+
description: "Array of standard codes to delete"
|
|
4133
|
+
},
|
|
4022
4134
|
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
4023
4135
|
}
|
|
4024
4136
|
},
|
|
@@ -4027,7 +4139,9 @@ var TOOL_DEFINITIONS = [
|
|
|
4027
4139
|
properties: {
|
|
4028
4140
|
success: { type: "boolean" },
|
|
4029
4141
|
id: { type: "string" },
|
|
4142
|
+
code: { type: "string" },
|
|
4030
4143
|
ids: { type: "array", items: { type: "string" } },
|
|
4144
|
+
codes: { type: "array", items: { type: "string" } },
|
|
4031
4145
|
repo: { type: "string" },
|
|
4032
4146
|
deletedCount: { type: "number" }
|
|
4033
4147
|
},
|
|
@@ -4150,7 +4264,10 @@ var TOOL_DEFINITIONS = [
|
|
|
4150
4264
|
doc_path: { type: "string" },
|
|
4151
4265
|
tags: { type: "array", items: { type: "string" } },
|
|
4152
4266
|
metadata: { type: "object" },
|
|
4153
|
-
parent_id: {
|
|
4267
|
+
parent_id: {
|
|
4268
|
+
type: "string",
|
|
4269
|
+
description: "Optional parent task ID (UUID) or parent task code (e.g. TASK-001). Resolved to UUID before storing."
|
|
4270
|
+
},
|
|
4154
4271
|
depends_on: { type: "string", format: "uuid" },
|
|
4155
4272
|
est_tokens: { type: "number", minimum: 0, description: "Estimated tokens budget for this task" },
|
|
4156
4273
|
tasks: {
|
|
@@ -4178,7 +4295,10 @@ var TOOL_DEFINITIONS = [
|
|
|
4178
4295
|
doc_path: { type: "string" },
|
|
4179
4296
|
tags: { type: "array", items: { type: "string" } },
|
|
4180
4297
|
metadata: { type: "object" },
|
|
4181
|
-
parent_id: {
|
|
4298
|
+
parent_id: {
|
|
4299
|
+
type: "string",
|
|
4300
|
+
description: "Optional parent task ID (UUID) or parent task code (e.g. TASK-001). Resolved to UUID before storing."
|
|
4301
|
+
},
|
|
4182
4302
|
depends_on: { type: "string", format: "uuid" },
|
|
4183
4303
|
est_tokens: { type: "number", minimum: 0 }
|
|
4184
4304
|
},
|
|
@@ -4250,7 +4370,10 @@ var TOOL_DEFINITIONS = [
|
|
|
4250
4370
|
doc_path: { type: "string" },
|
|
4251
4371
|
tags: { type: "array", items: { type: "string" } },
|
|
4252
4372
|
metadata: { type: "object" },
|
|
4253
|
-
parent_id: {
|
|
4373
|
+
parent_id: {
|
|
4374
|
+
type: "string",
|
|
4375
|
+
description: "Optional parent task ID (UUID) or parent task code (e.g. TASK-001). Resolved to UUID before storing."
|
|
4376
|
+
},
|
|
4254
4377
|
depends_on: { type: "string", format: "uuid" },
|
|
4255
4378
|
est_tokens: {
|
|
4256
4379
|
type: "number",
|
|
@@ -4306,8 +4429,13 @@ var TOOL_DEFINITIONS = [
|
|
|
4306
4429
|
type: "object",
|
|
4307
4430
|
properties: {
|
|
4308
4431
|
repo: { type: "string", description: "Repository name" },
|
|
4309
|
-
id: {
|
|
4432
|
+
id: {
|
|
4433
|
+
type: "string",
|
|
4434
|
+
format: "uuid",
|
|
4435
|
+
description: "Task ID (for single deletion). Optional if task_code is provided."
|
|
4436
|
+
},
|
|
4310
4437
|
ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk deletion)" },
|
|
4438
|
+
task_code: { type: "string", description: "Task code (e.g. TASK-001). Optional if id is provided." },
|
|
4311
4439
|
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
4312
4440
|
},
|
|
4313
4441
|
required: ["repo"]
|
|
@@ -4317,6 +4445,7 @@ var TOOL_DEFINITIONS = [
|
|
|
4317
4445
|
properties: {
|
|
4318
4446
|
success: { type: "boolean" },
|
|
4319
4447
|
id: { type: "string" },
|
|
4448
|
+
task_code: { type: "string" },
|
|
4320
4449
|
ids: { type: "array", items: { type: "string" } },
|
|
4321
4450
|
repo: { type: "string" },
|
|
4322
4451
|
deletedCount: { type: "number" }
|
|
@@ -4400,6 +4529,58 @@ var TOOL_DEFINITIONS = [
|
|
|
4400
4529
|
required: ["schema", "tasks", "count"]
|
|
4401
4530
|
}
|
|
4402
4531
|
},
|
|
4532
|
+
{
|
|
4533
|
+
name: "task-search",
|
|
4534
|
+
title: "Task Search",
|
|
4535
|
+
description: "Dedicated search tool for tasks. Returns a compact pointer table of matching tasks [id, task_code, title, status, priority, updated_at, phase]. Use task-detail for full task content.",
|
|
4536
|
+
annotations: {
|
|
4537
|
+
readOnlyHint: true,
|
|
4538
|
+
idempotentHint: true,
|
|
4539
|
+
openWorldHint: false
|
|
4540
|
+
},
|
|
4541
|
+
inputSchema: {
|
|
4542
|
+
type: "object",
|
|
4543
|
+
properties: {
|
|
4544
|
+
repo: { type: "string", description: "Repository name" },
|
|
4545
|
+
query: { type: "string", minLength: 1, description: "Search keyword matching task code, title, or description" },
|
|
4546
|
+
status: { type: "string", description: "Optional status filter (single or comma-separated)" },
|
|
4547
|
+
phase: { type: "string", description: "Filter by phase (e.g., 'research', 'implementation')" },
|
|
4548
|
+
priority: { type: "number", minimum: 1, maximum: 5, description: "Filter by priority (1-5)" },
|
|
4549
|
+
limit: { type: "number", minimum: 1, maximum: 100, default: 10 },
|
|
4550
|
+
offset: { type: "number", minimum: 0, default: 0 },
|
|
4551
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
4552
|
+
},
|
|
4553
|
+
required: ["repo", "query"]
|
|
4554
|
+
},
|
|
4555
|
+
outputSchema: {
|
|
4556
|
+
type: "object",
|
|
4557
|
+
properties: {
|
|
4558
|
+
schema: { type: "string", enum: ["task-search"] },
|
|
4559
|
+
query: { type: "string" },
|
|
4560
|
+
count: { type: "number", description: "Number of rows returned" },
|
|
4561
|
+
total: { type: "number", description: "Total matching tasks before pagination" },
|
|
4562
|
+
offset: { type: "number" },
|
|
4563
|
+
limit: { type: "number" },
|
|
4564
|
+
results: {
|
|
4565
|
+
type: "object",
|
|
4566
|
+
properties: {
|
|
4567
|
+
columns: {
|
|
4568
|
+
type: "array",
|
|
4569
|
+
items: { type: "string" },
|
|
4570
|
+
description: "Column names: [id, task_code, title, status, priority, updated_at, phase]"
|
|
4571
|
+
},
|
|
4572
|
+
rows: {
|
|
4573
|
+
type: "array",
|
|
4574
|
+
items: { type: "array" },
|
|
4575
|
+
description: "Each row: [id, task_code, title, status, priority, updated_at, phase]. Use task-detail to fetch full task."
|
|
4576
|
+
}
|
|
4577
|
+
},
|
|
4578
|
+
required: ["columns", "rows"]
|
|
4579
|
+
}
|
|
4580
|
+
},
|
|
4581
|
+
required: ["schema", "query", "count", "total", "offset", "limit", "results"]
|
|
4582
|
+
}
|
|
4583
|
+
},
|
|
4403
4584
|
{
|
|
4404
4585
|
name: "handoff-create",
|
|
4405
4586
|
title: "Handoff Create",
|
|
@@ -4762,7 +4943,8 @@ var TOOL_DEFINITIONS = [
|
|
|
4762
4943
|
inputSchema: {
|
|
4763
4944
|
type: "object",
|
|
4764
4945
|
properties: {
|
|
4765
|
-
id: { type: "string", description: "Standard ID to update" },
|
|
4946
|
+
id: { type: "string", format: "uuid", description: "Standard ID to update. Optional if code is provided." },
|
|
4947
|
+
code: { type: "string", maxLength: 20, description: "Short standard code. Optional if id is provided." },
|
|
4766
4948
|
name: { type: "string", minLength: 3, maxLength: 255 },
|
|
4767
4949
|
content: { type: "string", minLength: 10 },
|
|
4768
4950
|
parent_id: { type: "string", format: "uuid", nullable: true },
|
|
@@ -4777,8 +4959,7 @@ var TOOL_DEFINITIONS = [
|
|
|
4777
4959
|
agent: { type: "string" },
|
|
4778
4960
|
model: { type: "string" },
|
|
4779
4961
|
structured: { type: "boolean", default: false }
|
|
4780
|
-
}
|
|
4781
|
-
required: ["id"]
|
|
4962
|
+
}
|
|
4782
4963
|
},
|
|
4783
4964
|
outputSchema: {
|
|
4784
4965
|
type: "object",
|
|
@@ -5373,6 +5554,369 @@ async function completePromptArgument(name, argName, value, contextArguments, da
|
|
|
5373
5554
|
return [];
|
|
5374
5555
|
}
|
|
5375
5556
|
|
|
5557
|
+
// src/mcp/utils/mcp-response.ts
|
|
5558
|
+
import { z as z2 } from "zod";
|
|
5559
|
+
var McpAnnotationsSchema = z2.object({
|
|
5560
|
+
audience: z2.array(z2.enum(["user", "assistant"])).optional(),
|
|
5561
|
+
priority: z2.number().min(0).max(1).optional(),
|
|
5562
|
+
lastModified: z2.string().optional()
|
|
5563
|
+
}).strict().optional();
|
|
5564
|
+
var McpContentSchema = z2.discriminatedUnion("type", [
|
|
5565
|
+
z2.object({
|
|
5566
|
+
type: z2.literal("text"),
|
|
5567
|
+
text: z2.string(),
|
|
5568
|
+
annotations: McpAnnotationsSchema
|
|
5569
|
+
}),
|
|
5570
|
+
z2.object({
|
|
5571
|
+
type: z2.literal("image"),
|
|
5572
|
+
data: z2.string(),
|
|
5573
|
+
mimeType: z2.string(),
|
|
5574
|
+
annotations: McpAnnotationsSchema
|
|
5575
|
+
}),
|
|
5576
|
+
z2.object({
|
|
5577
|
+
type: z2.literal("resource"),
|
|
5578
|
+
resource: z2.object({
|
|
5579
|
+
uri: z2.string(),
|
|
5580
|
+
mimeType: z2.string().optional(),
|
|
5581
|
+
text: z2.string().optional(),
|
|
5582
|
+
annotations: McpAnnotationsSchema
|
|
5583
|
+
})
|
|
5584
|
+
})
|
|
5585
|
+
]);
|
|
5586
|
+
function createMcpResponse(data, summary, options) {
|
|
5587
|
+
const { structuredContentPathHint, contentSummary, includeSerializedStructuredContent = false } = options || {};
|
|
5588
|
+
void includeSerializedStructuredContent;
|
|
5589
|
+
let finalData = data;
|
|
5590
|
+
if (data && typeof data === "object") {
|
|
5591
|
+
const cloned = JSON.parse(JSON.stringify(data));
|
|
5592
|
+
finalData = cloned;
|
|
5593
|
+
const arrayKeys = ["results", "tasks", "memories", "items"];
|
|
5594
|
+
let foundArray = false;
|
|
5595
|
+
for (const key of arrayKeys) {
|
|
5596
|
+
const value = cloned[key];
|
|
5597
|
+
if (Array.isArray(value)) {
|
|
5598
|
+
cloned[key] = value.map(
|
|
5599
|
+
(item) => pruneMetadata(item)
|
|
5600
|
+
);
|
|
5601
|
+
foundArray = true;
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
if (Array.isArray(cloned)) {
|
|
5605
|
+
finalData = cloned.map((item) => pruneMetadata(item));
|
|
5606
|
+
} else if (!foundArray) {
|
|
5607
|
+
finalData = pruneMetadata(cloned);
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
const content = [];
|
|
5611
|
+
if (contentSummary && contentSummary.trim().length > 0) {
|
|
5612
|
+
content.push({
|
|
5613
|
+
type: "text",
|
|
5614
|
+
text: contentSummary.trim()
|
|
5615
|
+
});
|
|
5616
|
+
} else if (summary && summary.trim().length > 0) {
|
|
5617
|
+
const pointerText = structuredContentPathHint ? `Read structuredContent.${structuredContentPathHint} for details.` : `Read structuredContent for machine-readable results.`;
|
|
5618
|
+
content.push({
|
|
5619
|
+
type: "text",
|
|
5620
|
+
text: `${summary.trim()} ${pointerText}`
|
|
5621
|
+
});
|
|
5622
|
+
}
|
|
5623
|
+
const response = {
|
|
5624
|
+
structuredContent: finalData,
|
|
5625
|
+
isError: false
|
|
5626
|
+
};
|
|
5627
|
+
if (includeSerializedStructuredContent === false) {
|
|
5628
|
+
delete response.structuredContent;
|
|
5629
|
+
}
|
|
5630
|
+
response.content = content;
|
|
5631
|
+
return response;
|
|
5632
|
+
}
|
|
5633
|
+
function pruneMetadata(item) {
|
|
5634
|
+
if (!item || typeof item !== "object") return item;
|
|
5635
|
+
const pruned = { ...item };
|
|
5636
|
+
const toRemove = [
|
|
5637
|
+
"hit_count",
|
|
5638
|
+
"recall_count",
|
|
5639
|
+
"last_used_at",
|
|
5640
|
+
"expires_at",
|
|
5641
|
+
"agent",
|
|
5642
|
+
"role",
|
|
5643
|
+
"model",
|
|
5644
|
+
"recall_rate",
|
|
5645
|
+
"vector_version",
|
|
5646
|
+
"similarity"
|
|
5647
|
+
// Similarity is useful but adds noise if many results
|
|
5648
|
+
];
|
|
5649
|
+
for (const field of toRemove) {
|
|
5650
|
+
delete pruned[field];
|
|
5651
|
+
}
|
|
5652
|
+
return pruned;
|
|
5653
|
+
}
|
|
5654
|
+
function getPrimaryTextContent(response) {
|
|
5655
|
+
if (!Array.isArray(response.content)) return "";
|
|
5656
|
+
const textItem = response.content.find((item) => item.type === "text");
|
|
5657
|
+
return textItem?.type === "text" ? textItem.text : "";
|
|
5658
|
+
}
|
|
5659
|
+
|
|
5660
|
+
// src/mcp/tools/handoff.manage.ts
|
|
5661
|
+
function buildHandoffListSummary(repo, count, status, fromAgent, toAgent) {
|
|
5662
|
+
const parts = [`Found ${count} handoff${count === 1 ? "" : "s"} in repo "${repo}".`];
|
|
5663
|
+
if (status) {
|
|
5664
|
+
parts.push(`Status filter: ${status}.`);
|
|
5665
|
+
}
|
|
5666
|
+
if (fromAgent) {
|
|
5667
|
+
parts.push(`From agent: ${fromAgent}.`);
|
|
5668
|
+
}
|
|
5669
|
+
if (toAgent) {
|
|
5670
|
+
parts.push(`To agent: ${toAgent}.`);
|
|
5671
|
+
}
|
|
5672
|
+
return parts.join("\n");
|
|
5673
|
+
}
|
|
5674
|
+
function buildClaimListSummary(repo, count, agent, activeOnly) {
|
|
5675
|
+
const parts = [`Found ${count} claim${count === 1 ? "" : "s"} in repo "${repo}".`];
|
|
5676
|
+
if (agent) {
|
|
5677
|
+
parts.push(`Agent filter: ${agent}.`);
|
|
5678
|
+
}
|
|
5679
|
+
if (activeOnly) {
|
|
5680
|
+
parts.push("Showing active claims only.");
|
|
5681
|
+
}
|
|
5682
|
+
return parts.join("\n");
|
|
5683
|
+
}
|
|
5684
|
+
async function handleHandoffCreate(args, storage) {
|
|
5685
|
+
const validated = HandoffCreateSchema.parse(args);
|
|
5686
|
+
const { repo, from_agent, to_agent, task_id, task_code, summary, context, expires_at, structured } = validated;
|
|
5687
|
+
let resolvedTaskId = task_id ?? null;
|
|
5688
|
+
if (!resolvedTaskId && task_code) {
|
|
5689
|
+
const task = storage.tasks.getTaskByCode(repo, task_code);
|
|
5690
|
+
if (!task) {
|
|
5691
|
+
throw new Error(`Task not found: ${task_code} in repo ${repo}`);
|
|
5692
|
+
}
|
|
5693
|
+
resolvedTaskId = task.id;
|
|
5694
|
+
}
|
|
5695
|
+
const handoff = storage.handoffs.createHandoff({
|
|
5696
|
+
repo,
|
|
5697
|
+
from_agent,
|
|
5698
|
+
to_agent,
|
|
5699
|
+
task_id: resolvedTaskId,
|
|
5700
|
+
summary,
|
|
5701
|
+
context,
|
|
5702
|
+
expires_at
|
|
5703
|
+
});
|
|
5704
|
+
const contentSummary = [
|
|
5705
|
+
`Created handoff ${handoff.id}.`,
|
|
5706
|
+
`Repo: ${handoff.repo}`,
|
|
5707
|
+
`From: ${handoff.from_agent}`,
|
|
5708
|
+
`To: ${handoff.to_agent || "unassigned"}`,
|
|
5709
|
+
`Status: ${handoff.status}`,
|
|
5710
|
+
`Task ID: ${handoff.task_id || "-"}`,
|
|
5711
|
+
`Summary: ${handoff.summary}`
|
|
5712
|
+
].join("\n");
|
|
5713
|
+
return createMcpResponse(handoff, contentSummary, {
|
|
5714
|
+
contentSummary,
|
|
5715
|
+
includeSerializedStructuredContent: structured
|
|
5716
|
+
});
|
|
5717
|
+
}
|
|
5718
|
+
async function handleHandoffList(args, storage) {
|
|
5719
|
+
const validated = HandoffListSchema.parse(args);
|
|
5720
|
+
const { repo, status, from_agent, to_agent, limit, offset, structured } = validated;
|
|
5721
|
+
const handoffs = storage.handoffs.listHandoffs({
|
|
5722
|
+
repo,
|
|
5723
|
+
status,
|
|
5724
|
+
from_agent,
|
|
5725
|
+
to_agent,
|
|
5726
|
+
limit,
|
|
5727
|
+
offset
|
|
5728
|
+
});
|
|
5729
|
+
const COLUMNS = [
|
|
5730
|
+
"id",
|
|
5731
|
+
"from_agent",
|
|
5732
|
+
"to_agent",
|
|
5733
|
+
"task_id",
|
|
5734
|
+
"task_code",
|
|
5735
|
+
"status",
|
|
5736
|
+
"created_at",
|
|
5737
|
+
"updated_at",
|
|
5738
|
+
"expires_at",
|
|
5739
|
+
"summary",
|
|
5740
|
+
"context"
|
|
5741
|
+
];
|
|
5742
|
+
const rows = handoffs.map((handoff) => [
|
|
5743
|
+
handoff.id,
|
|
5744
|
+
handoff.from_agent,
|
|
5745
|
+
handoff.to_agent,
|
|
5746
|
+
handoff.task_id,
|
|
5747
|
+
handoff.task_code ?? null,
|
|
5748
|
+
handoff.status,
|
|
5749
|
+
handoff.created_at,
|
|
5750
|
+
handoff.updated_at,
|
|
5751
|
+
handoff.expires_at,
|
|
5752
|
+
handoff.summary,
|
|
5753
|
+
handoff.context
|
|
5754
|
+
]);
|
|
5755
|
+
const structuredData = {
|
|
5756
|
+
schema: "handoff-list",
|
|
5757
|
+
handoffs: {
|
|
5758
|
+
columns: [...COLUMNS],
|
|
5759
|
+
rows
|
|
5760
|
+
},
|
|
5761
|
+
count: rows.length,
|
|
5762
|
+
offset
|
|
5763
|
+
};
|
|
5764
|
+
const contentSummary = buildHandoffListSummary(repo, rows.length, status, from_agent, to_agent);
|
|
5765
|
+
return createMcpResponse(structuredData, contentSummary, {
|
|
5766
|
+
contentSummary,
|
|
5767
|
+
includeSerializedStructuredContent: structured
|
|
5768
|
+
});
|
|
5769
|
+
}
|
|
5770
|
+
async function handleHandoffUpdate(args, storage) {
|
|
5771
|
+
const validated = HandoffUpdateSchema.parse(args);
|
|
5772
|
+
const { id, status, structured } = validated;
|
|
5773
|
+
const existing = storage.handoffs.getHandoffById(id);
|
|
5774
|
+
if (!existing) {
|
|
5775
|
+
throw new Error(`Handoff not found: ${id}`);
|
|
5776
|
+
}
|
|
5777
|
+
const success = storage.handoffs.updateHandoffStatus(id, status);
|
|
5778
|
+
if (!success) {
|
|
5779
|
+
throw new Error(`Failed to update handoff: ${id}`);
|
|
5780
|
+
}
|
|
5781
|
+
const updated = storage.handoffs.getHandoffById(id);
|
|
5782
|
+
const result = {
|
|
5783
|
+
success,
|
|
5784
|
+
id,
|
|
5785
|
+
status,
|
|
5786
|
+
handoff: updated
|
|
5787
|
+
};
|
|
5788
|
+
const contentSummary = [`Updated handoff ${id}.`, `Status: ${status}`].join("\n");
|
|
5789
|
+
return createMcpResponse(result, contentSummary, {
|
|
5790
|
+
contentSummary,
|
|
5791
|
+
includeSerializedStructuredContent: structured
|
|
5792
|
+
});
|
|
5793
|
+
}
|
|
5794
|
+
async function handleTaskClaim(args, storage) {
|
|
5795
|
+
const validated = TaskClaimSchema.parse(args);
|
|
5796
|
+
const { repo, task_id, task_code, agent, role, metadata, structured } = validated;
|
|
5797
|
+
let taskId = task_id;
|
|
5798
|
+
let resolvedTaskCode;
|
|
5799
|
+
if (taskId) {
|
|
5800
|
+
const task = storage.tasks.getTaskById(taskId);
|
|
5801
|
+
if (!task || task.repo !== repo) {
|
|
5802
|
+
throw new Error(`Task not found: ${taskId} in repo ${repo}`);
|
|
5803
|
+
}
|
|
5804
|
+
resolvedTaskCode = task.task_code;
|
|
5805
|
+
} else if (task_code) {
|
|
5806
|
+
const task = storage.tasks.getTaskByCode(repo, task_code);
|
|
5807
|
+
if (!task) {
|
|
5808
|
+
throw new Error(`Task not found: ${task_code} in repo ${repo}`);
|
|
5809
|
+
}
|
|
5810
|
+
taskId = task.id;
|
|
5811
|
+
resolvedTaskCode = task.task_code;
|
|
5812
|
+
} else {
|
|
5813
|
+
throw new Error("Either task_id or task_code must be provided");
|
|
5814
|
+
}
|
|
5815
|
+
const claim = storage.handoffs.claimTask({
|
|
5816
|
+
repo,
|
|
5817
|
+
task_id: taskId,
|
|
5818
|
+
agent,
|
|
5819
|
+
role,
|
|
5820
|
+
metadata
|
|
5821
|
+
});
|
|
5822
|
+
const responseData = {
|
|
5823
|
+
...claim,
|
|
5824
|
+
task_code: resolvedTaskCode
|
|
5825
|
+
};
|
|
5826
|
+
const contentSummary = [
|
|
5827
|
+
`Claimed task ${resolvedTaskCode || claim.task_id}.`,
|
|
5828
|
+
`Repo: ${claim.repo}`,
|
|
5829
|
+
`Task ID: ${claim.task_id}`,
|
|
5830
|
+
`Agent: ${claim.agent}`,
|
|
5831
|
+
`Role: ${claim.role}`,
|
|
5832
|
+
`Claimed At: ${claim.claimed_at}`
|
|
5833
|
+
].join("\n");
|
|
5834
|
+
const response = createMcpResponse(responseData, contentSummary, {
|
|
5835
|
+
contentSummary,
|
|
5836
|
+
includeSerializedStructuredContent: structured
|
|
5837
|
+
});
|
|
5838
|
+
if (structured) {
|
|
5839
|
+
response.structuredContent = responseData;
|
|
5840
|
+
}
|
|
5841
|
+
return response;
|
|
5842
|
+
}
|
|
5843
|
+
async function handleClaimList(args, storage) {
|
|
5844
|
+
const validated = ClaimListSchema.parse(args);
|
|
5845
|
+
const { repo, agent, active_only, limit, offset, structured } = validated;
|
|
5846
|
+
const claims = storage.handoffs.listClaims({
|
|
5847
|
+
repo,
|
|
5848
|
+
agent,
|
|
5849
|
+
active_only,
|
|
5850
|
+
limit,
|
|
5851
|
+
offset
|
|
5852
|
+
});
|
|
5853
|
+
const COLUMNS = ["id", "task_id", "task_code", "agent", "role", "claimed_at", "released_at", "metadata"];
|
|
5854
|
+
const rows = claims.map((claim) => [
|
|
5855
|
+
claim.id,
|
|
5856
|
+
claim.task_id,
|
|
5857
|
+
claim.task_code ?? null,
|
|
5858
|
+
claim.agent,
|
|
5859
|
+
claim.role,
|
|
5860
|
+
claim.claimed_at,
|
|
5861
|
+
claim.released_at,
|
|
5862
|
+
claim.metadata
|
|
5863
|
+
]);
|
|
5864
|
+
const structuredData = {
|
|
5865
|
+
schema: "claim-list",
|
|
5866
|
+
claims: {
|
|
5867
|
+
columns: [...COLUMNS],
|
|
5868
|
+
rows
|
|
5869
|
+
},
|
|
5870
|
+
count: rows.length,
|
|
5871
|
+
offset
|
|
5872
|
+
};
|
|
5873
|
+
const contentSummary = buildClaimListSummary(repo, rows.length, agent, active_only);
|
|
5874
|
+
return createMcpResponse(structuredData, contentSummary, {
|
|
5875
|
+
contentSummary,
|
|
5876
|
+
includeSerializedStructuredContent: structured
|
|
5877
|
+
});
|
|
5878
|
+
}
|
|
5879
|
+
async function handleClaimRelease(args, storage) {
|
|
5880
|
+
const validated = ClaimReleaseSchema.parse(args);
|
|
5881
|
+
const { repo, task_id, task_code, agent, structured } = validated;
|
|
5882
|
+
let resolvedTaskId = task_id;
|
|
5883
|
+
let resolvedTaskCode = task_code ?? null;
|
|
5884
|
+
if (resolvedTaskId) {
|
|
5885
|
+
const task = storage.tasks.getTaskById(resolvedTaskId);
|
|
5886
|
+
if (!task || task.repo !== repo) {
|
|
5887
|
+
throw new Error(`Task not found: ${resolvedTaskId} in repo ${repo}`);
|
|
5888
|
+
}
|
|
5889
|
+
resolvedTaskCode = task.task_code;
|
|
5890
|
+
} else if (task_code) {
|
|
5891
|
+
const task = storage.tasks.getTaskByCode(repo, task_code);
|
|
5892
|
+
if (!task) {
|
|
5893
|
+
throw new Error(`Task not found: ${task_code} in repo ${repo}`);
|
|
5894
|
+
}
|
|
5895
|
+
resolvedTaskId = task.id;
|
|
5896
|
+
resolvedTaskCode = task.task_code;
|
|
5897
|
+
}
|
|
5898
|
+
const success = storage.handoffs.releaseClaim(resolvedTaskId, agent);
|
|
5899
|
+
if (!success) {
|
|
5900
|
+
throw new Error(`No active claim found for task ${resolvedTaskCode || resolvedTaskId}`);
|
|
5901
|
+
}
|
|
5902
|
+
const result = {
|
|
5903
|
+
success,
|
|
5904
|
+
repo,
|
|
5905
|
+
task_id: resolvedTaskId,
|
|
5906
|
+
task_code: resolvedTaskCode,
|
|
5907
|
+
agent: agent ?? null
|
|
5908
|
+
};
|
|
5909
|
+
const contentSummary = [
|
|
5910
|
+
`Released claim for task ${resolvedTaskCode || resolvedTaskId}.`,
|
|
5911
|
+
`Repo: ${repo}`,
|
|
5912
|
+
agent ? `Agent: ${agent}` : "Agent: any active claimant"
|
|
5913
|
+
].join("\n");
|
|
5914
|
+
return createMcpResponse(result, contentSummary, {
|
|
5915
|
+
contentSummary,
|
|
5916
|
+
includeSerializedStructuredContent: structured
|
|
5917
|
+
});
|
|
5918
|
+
}
|
|
5919
|
+
|
|
5376
5920
|
// src/mcp/tools/standard.shared.ts
|
|
5377
5921
|
function toContextSlug(value) {
|
|
5378
5922
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -5414,17 +5958,12 @@ export {
|
|
|
5414
5958
|
TaskCreateInteractiveSchema,
|
|
5415
5959
|
TaskUpdateSchema,
|
|
5416
5960
|
TaskListSchema,
|
|
5961
|
+
TaskSearchSchema,
|
|
5417
5962
|
TaskDeleteSchema,
|
|
5418
5963
|
MemoryDetailSchema,
|
|
5419
5964
|
StandardDetailSchema,
|
|
5420
5965
|
StandardDeleteSchema,
|
|
5421
5966
|
TaskGetSchema,
|
|
5422
|
-
HandoffCreateSchema,
|
|
5423
|
-
HandoffUpdateSchema,
|
|
5424
|
-
HandoffListSchema,
|
|
5425
|
-
TaskClaimSchema,
|
|
5426
|
-
ClaimListSchema,
|
|
5427
|
-
ClaimReleaseSchema,
|
|
5428
5967
|
StandardStoreSchema,
|
|
5429
5968
|
StandardUpdateSchema,
|
|
5430
5969
|
StandardSearchSchema,
|
|
@@ -5447,6 +5986,14 @@ export {
|
|
|
5447
5986
|
listPrompts,
|
|
5448
5987
|
getPrompt,
|
|
5449
5988
|
completePromptArgument,
|
|
5989
|
+
createMcpResponse,
|
|
5990
|
+
getPrimaryTextContent,
|
|
5991
|
+
handleHandoffCreate,
|
|
5992
|
+
handleHandoffList,
|
|
5993
|
+
handleHandoffUpdate,
|
|
5994
|
+
handleTaskClaim,
|
|
5995
|
+
handleClaimList,
|
|
5996
|
+
handleClaimRelease,
|
|
5450
5997
|
toContextSlug,
|
|
5451
5998
|
buildStandardVectorText
|
|
5452
5999
|
};
|