@vheins/local-memory-mcp 0.8.4 → 0.8.5
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/{chunk-E6BKW23Y.js → chunk-NQHH7CDG.js} +1429 -1407
- package/dist/dashboard/server.js +1 -1
- package/dist/mcp/server.js +4 -1
- package/package.json +1 -1
|
@@ -1,4 +1,41 @@
|
|
|
1
|
+
// src/mcp/capabilities.ts
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
var pkgPath = path.join(__dirname, "../../package.json");
|
|
7
|
+
var pkgVersion = "0.1.0";
|
|
8
|
+
try {
|
|
9
|
+
if (fs.existsSync(pkgPath)) {
|
|
10
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
11
|
+
pkgVersion = pkg.version;
|
|
12
|
+
}
|
|
13
|
+
} catch {
|
|
14
|
+
}
|
|
15
|
+
var MCP_PROTOCOL_VERSION = "2025-11-25";
|
|
16
|
+
var CAPABILITIES = {
|
|
17
|
+
serverInfo: {
|
|
18
|
+
name: "mcp-memory-local",
|
|
19
|
+
version: pkgVersion
|
|
20
|
+
},
|
|
21
|
+
capabilities: {
|
|
22
|
+
completions: {},
|
|
23
|
+
logging: {},
|
|
24
|
+
resources: {
|
|
25
|
+
subscribe: true,
|
|
26
|
+
listChanged: true
|
|
27
|
+
},
|
|
28
|
+
tools: {
|
|
29
|
+
listChanged: false
|
|
30
|
+
},
|
|
31
|
+
prompts: {
|
|
32
|
+
listChanged: true
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
1
37
|
// src/mcp/utils/logger.ts
|
|
38
|
+
import fs2 from "fs";
|
|
2
39
|
var LEVELS = {
|
|
3
40
|
debug: 0,
|
|
4
41
|
info: 1,
|
|
@@ -134,113 +171,31 @@ function addLogSink(sink) {
|
|
|
134
171
|
return () => sinks.delete(sink);
|
|
135
172
|
}
|
|
136
173
|
var LOG_LEVEL_VALUES = Object.keys(LEVELS);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
function createSessionContext() {
|
|
142
|
-
return {
|
|
143
|
-
roots: [],
|
|
144
|
-
supportsRoots: false,
|
|
145
|
-
supportsSampling: false,
|
|
146
|
-
supportsSamplingTools: false,
|
|
147
|
-
supportsElicitation: false,
|
|
148
|
-
supportsElicitationForm: false,
|
|
149
|
-
supportsElicitationUrl: false
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
function updateSessionFromInitialize(session, params) {
|
|
153
|
-
const capabilities = params?.capabilities || {};
|
|
154
|
-
session.clientInfo = params?.clientInfo;
|
|
155
|
-
session.clientCapabilities = capabilities;
|
|
156
|
-
session.supportsRoots = Boolean(capabilities.roots);
|
|
157
|
-
session.supportsSampling = Boolean(capabilities.sampling);
|
|
158
|
-
const sampling = capabilities.sampling;
|
|
159
|
-
session.supportsSamplingTools = Boolean(sampling?.tools);
|
|
160
|
-
session.supportsElicitation = Boolean(capabilities.elicitation);
|
|
161
|
-
session.supportsElicitationForm = supportsElicitationMode(capabilities.elicitation, "form");
|
|
162
|
-
session.supportsElicitationUrl = supportsElicitationMode(capabilities.elicitation, "url");
|
|
163
|
-
}
|
|
164
|
-
function supportsElicitationMode(capability, mode) {
|
|
165
|
-
if (!capability || typeof capability !== "object") {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
const cap = capability;
|
|
169
|
-
if (mode === "form") {
|
|
170
|
-
return Object.keys(cap).length === 0 || typeof cap.form === "object";
|
|
171
|
-
}
|
|
172
|
-
return typeof cap.url === "object";
|
|
173
|
-
}
|
|
174
|
-
function updateSessionRoots(session, roots) {
|
|
175
|
-
const normalized = normalizeRoots(roots);
|
|
176
|
-
const previous = JSON.stringify(session.roots);
|
|
177
|
-
const next = JSON.stringify(normalized);
|
|
178
|
-
session.roots = normalized;
|
|
179
|
-
return previous !== next;
|
|
180
|
-
}
|
|
181
|
-
function normalizeRoots(roots) {
|
|
182
|
-
if (!Array.isArray(roots)) return [];
|
|
183
|
-
const seen = /* @__PURE__ */ new Set();
|
|
184
|
-
const normalized = [];
|
|
185
|
-
for (const root of roots) {
|
|
186
|
-
if (!root || typeof root !== "object") continue;
|
|
187
|
-
const r = root;
|
|
188
|
-
const uri = typeof r.uri === "string" ? r.uri : void 0;
|
|
189
|
-
const name = typeof r.name === "string" ? r.name : void 0;
|
|
190
|
-
if (!uri || seen.has(uri)) continue;
|
|
191
|
-
seen.add(uri);
|
|
192
|
-
normalized.push({ uri, name });
|
|
193
|
-
}
|
|
194
|
-
return normalized;
|
|
195
|
-
}
|
|
196
|
-
function extractRootsFromResult(result) {
|
|
197
|
-
return normalizeRoots(result?.roots);
|
|
198
|
-
}
|
|
199
|
-
function getFilesystemRoots(session) {
|
|
200
|
-
if (!session) return [];
|
|
201
|
-
const resolved = [];
|
|
202
|
-
for (const root of session.roots) {
|
|
203
|
-
if (!root.uri.startsWith("file://")) continue;
|
|
174
|
+
function createFileSink(logDir, maxFiles = 5) {
|
|
175
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
176
|
+
const existing = fs2.readdirSync(logDir).filter((f) => f.startsWith("mcp-") && f.endsWith(".log")).sort();
|
|
177
|
+
while (existing.length >= maxFiles) {
|
|
204
178
|
try {
|
|
205
|
-
|
|
179
|
+
fs2.unlinkSync(`${logDir}/${existing.shift()}`);
|
|
206
180
|
} catch {
|
|
207
181
|
}
|
|
208
182
|
}
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
function findContainingRoot(targetPath, session) {
|
|
221
|
-
const roots = getFilesystemRoots(session);
|
|
222
|
-
if (roots.length === 0) return null;
|
|
223
|
-
const normalizedTarget = path.resolve(targetPath);
|
|
224
|
-
for (const rootPath of roots) {
|
|
225
|
-
const relative = path.relative(rootPath, normalizedTarget);
|
|
226
|
-
if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
227
|
-
return rootPath;
|
|
183
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
184
|
+
const logFile = `${logDir}/mcp-${timestamp}.log`;
|
|
185
|
+
return (payload) => {
|
|
186
|
+
const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${payload.level.toUpperCase()}] ${JSON.stringify(payload.data)}
|
|
187
|
+
`;
|
|
188
|
+
try {
|
|
189
|
+
fs2.appendFileSync(logFile, line);
|
|
190
|
+
} catch {
|
|
228
191
|
}
|
|
229
|
-
}
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
function inferRepoFromSession(session) {
|
|
233
|
-
const roots = getFilesystemRoots(session);
|
|
234
|
-
if (roots.length === 1) {
|
|
235
|
-
return path.basename(roots[0]);
|
|
236
|
-
}
|
|
237
|
-
return void 0;
|
|
192
|
+
};
|
|
238
193
|
}
|
|
239
194
|
|
|
240
195
|
// src/mcp/storage/sqlite.ts
|
|
241
196
|
import Database from "better-sqlite3";
|
|
242
197
|
import path3 from "path";
|
|
243
|
-
import
|
|
198
|
+
import fs4 from "fs";
|
|
244
199
|
import os from "os";
|
|
245
200
|
|
|
246
201
|
// src/mcp/storage/migrations.ts
|
|
@@ -1962,7 +1917,7 @@ var SummaryEntity = class extends BaseEntity {
|
|
|
1962
1917
|
// src/mcp/storage/write-lock.ts
|
|
1963
1918
|
import lockfile from "proper-lockfile";
|
|
1964
1919
|
import path2 from "path";
|
|
1965
|
-
import
|
|
1920
|
+
import fs3 from "fs";
|
|
1966
1921
|
var LOCK_STALE_MS = 15e3;
|
|
1967
1922
|
var LOCK_RETRY_DELAY_MS = 100;
|
|
1968
1923
|
var LOCK_RETRY_COUNT = 150;
|
|
@@ -1971,9 +1926,9 @@ var WriteLock = class {
|
|
|
1971
1926
|
locked = false;
|
|
1972
1927
|
constructor(dbPath) {
|
|
1973
1928
|
this.lockTarget = dbPath;
|
|
1974
|
-
if (!
|
|
1975
|
-
|
|
1976
|
-
|
|
1929
|
+
if (!fs3.existsSync(dbPath)) {
|
|
1930
|
+
fs3.mkdirSync(path2.dirname(dbPath), { recursive: true });
|
|
1931
|
+
fs3.writeFileSync(dbPath, "");
|
|
1977
1932
|
}
|
|
1978
1933
|
}
|
|
1979
1934
|
/**
|
|
@@ -2027,11 +1982,11 @@ function resolveDbPath() {
|
|
|
2027
1982
|
if (process.env.MEMORY_DB_PATH) return process.env.MEMORY_DB_PATH;
|
|
2028
1983
|
const standardConfigDir = process.platform === "win32" ? path3.join(os.homedir(), ".local-memory-mcp") : process.platform === "darwin" ? path3.join(os.homedir(), "Library", "Application Support", "local-memory-mcp") : path3.join(os.homedir(), ".config", "local-memory-mcp");
|
|
2029
1984
|
const standardPath = path3.join(standardConfigDir, "memory.db");
|
|
2030
|
-
if (
|
|
1985
|
+
if (fs4.existsSync(standardPath)) return standardPath;
|
|
2031
1986
|
const legacyPath = path3.join(os.homedir(), ".config", "local-memory-mcp", "memory.db");
|
|
2032
|
-
if (
|
|
1987
|
+
if (fs4.existsSync(legacyPath)) return legacyPath;
|
|
2033
1988
|
const localCwdFile = path3.join(process.cwd(), "storage", "memory.db");
|
|
2034
|
-
if (
|
|
1989
|
+
if (fs4.existsSync(localCwdFile)) return localCwdFile;
|
|
2035
1990
|
return standardPath;
|
|
2036
1991
|
}
|
|
2037
1992
|
var DB_PATH = resolveDbPath();
|
|
@@ -2049,8 +2004,8 @@ var SQLiteStore = class _SQLiteStore {
|
|
|
2049
2004
|
this.dbPathInstance = finalPath;
|
|
2050
2005
|
if (finalPath !== ":memory:") {
|
|
2051
2006
|
const dbDir = path3.dirname(finalPath);
|
|
2052
|
-
if (!
|
|
2053
|
-
|
|
2007
|
+
if (!fs4.existsSync(dbDir)) {
|
|
2008
|
+
fs4.mkdirSync(dbDir, { recursive: true });
|
|
2054
2009
|
}
|
|
2055
2010
|
}
|
|
2056
2011
|
this.db = new Database(finalPath);
|
|
@@ -2102,12 +2057,12 @@ var SQLiteStore = class _SQLiteStore {
|
|
|
2102
2057
|
*/
|
|
2103
2058
|
_attemptRecovery(dbPath) {
|
|
2104
2059
|
const backupPath = dbPath + ".backup";
|
|
2105
|
-
if (
|
|
2060
|
+
if (fs4.existsSync(backupPath)) {
|
|
2106
2061
|
logger.warn("[SQLiteStore] Attempting recovery from backup", { backupPath });
|
|
2107
2062
|
try {
|
|
2108
2063
|
const corruptPath = `${dbPath}.corrupt_${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").slice(0, 15)}`;
|
|
2109
|
-
|
|
2110
|
-
|
|
2064
|
+
fs4.copyFileSync(dbPath, corruptPath);
|
|
2065
|
+
fs4.copyFileSync(backupPath, dbPath);
|
|
2111
2066
|
logger.warn("[SQLiteStore] Recovery successful. Corrupt file saved to", { corruptPath });
|
|
2112
2067
|
} catch (err) {
|
|
2113
2068
|
logger.error("[SQLiteStore] Recovery failed", { error: String(err) });
|
|
@@ -2125,7 +2080,7 @@ var SQLiteStore = class _SQLiteStore {
|
|
|
2125
2080
|
try {
|
|
2126
2081
|
this.db.pragma("wal_checkpoint(PASSIVE)");
|
|
2127
2082
|
const backupPath = this.dbPathInstance + ".backup";
|
|
2128
|
-
|
|
2083
|
+
fs4.copyFileSync(this.dbPathInstance, backupPath);
|
|
2129
2084
|
} catch (err) {
|
|
2130
2085
|
logger.warn("[SQLiteStore] Backup failed", { error: String(err) });
|
|
2131
2086
|
}
|
|
@@ -2170,606 +2125,106 @@ var SQLiteStore = class _SQLiteStore {
|
|
|
2170
2125
|
}
|
|
2171
2126
|
};
|
|
2172
2127
|
|
|
2173
|
-
// src/mcp/
|
|
2174
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2128
|
+
// src/mcp/session.ts
|
|
2175
2129
|
import path4 from "path";
|
|
2176
|
-
import
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2130
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2131
|
+
function createSessionContext() {
|
|
2132
|
+
return {
|
|
2133
|
+
roots: [],
|
|
2134
|
+
supportsRoots: false,
|
|
2135
|
+
supportsSampling: false,
|
|
2136
|
+
supportsSamplingTools: false,
|
|
2137
|
+
supportsElicitation: false,
|
|
2138
|
+
supportsElicitationForm: false,
|
|
2139
|
+
supportsElicitationUrl: false
|
|
2140
|
+
};
|
|
2186
2141
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
capabilities
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
listChanged: true
|
|
2199
|
-
},
|
|
2200
|
-
tools: {
|
|
2201
|
-
listChanged: false
|
|
2202
|
-
},
|
|
2203
|
-
prompts: {
|
|
2204
|
-
listChanged: true
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
};
|
|
2208
|
-
|
|
2209
|
-
// src/mcp/utils/pagination.ts
|
|
2210
|
-
function encodeCursor(offset) {
|
|
2211
|
-
return Buffer.from(String(offset), "utf8").toString("base64");
|
|
2142
|
+
function updateSessionFromInitialize(session, params) {
|
|
2143
|
+
const capabilities = params?.capabilities || {};
|
|
2144
|
+
session.clientInfo = params?.clientInfo;
|
|
2145
|
+
session.clientCapabilities = capabilities;
|
|
2146
|
+
session.supportsRoots = Boolean(capabilities.roots);
|
|
2147
|
+
session.supportsSampling = Boolean(capabilities.sampling);
|
|
2148
|
+
const sampling = capabilities.sampling;
|
|
2149
|
+
session.supportsSamplingTools = Boolean(sampling?.tools);
|
|
2150
|
+
session.supportsElicitation = Boolean(capabilities.elicitation);
|
|
2151
|
+
session.supportsElicitationForm = supportsElicitationMode(capabilities.elicitation, "form");
|
|
2152
|
+
session.supportsElicitationUrl = supportsElicitationMode(capabilities.elicitation, "url");
|
|
2212
2153
|
}
|
|
2213
|
-
function
|
|
2214
|
-
if (
|
|
2215
|
-
return
|
|
2216
|
-
}
|
|
2217
|
-
if (typeof cursor !== "string" || cursor.trim() === "") {
|
|
2218
|
-
throw invalidPaginationParams("Invalid cursor");
|
|
2219
|
-
}
|
|
2220
|
-
let decoded;
|
|
2221
|
-
try {
|
|
2222
|
-
decoded = Buffer.from(cursor, "base64").toString("utf8");
|
|
2223
|
-
} catch {
|
|
2224
|
-
throw invalidPaginationParams("Invalid cursor");
|
|
2225
|
-
}
|
|
2226
|
-
if (!/^\d+$/.test(decoded)) {
|
|
2227
|
-
throw invalidPaginationParams("Invalid cursor");
|
|
2154
|
+
function supportsElicitationMode(capability, mode) {
|
|
2155
|
+
if (!capability || typeof capability !== "object") {
|
|
2156
|
+
return false;
|
|
2228
2157
|
}
|
|
2229
|
-
const
|
|
2230
|
-
if (
|
|
2231
|
-
|
|
2158
|
+
const cap = capability;
|
|
2159
|
+
if (mode === "form") {
|
|
2160
|
+
return Object.keys(cap).length === 0 || typeof cap.form === "object";
|
|
2232
2161
|
}
|
|
2233
|
-
return
|
|
2162
|
+
return typeof cap.url === "object";
|
|
2234
2163
|
}
|
|
2235
|
-
function
|
|
2236
|
-
const
|
|
2237
|
-
|
|
2238
|
-
|
|
2164
|
+
function updateSessionRoots(session, roots) {
|
|
2165
|
+
const normalized = normalizeRoots(roots);
|
|
2166
|
+
const previous = JSON.stringify(session.roots);
|
|
2167
|
+
const next = JSON.stringify(normalized);
|
|
2168
|
+
session.roots = normalized;
|
|
2169
|
+
return previous !== next;
|
|
2239
2170
|
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
const
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2171
|
+
function normalizeRoots(roots) {
|
|
2172
|
+
if (!Array.isArray(roots)) return [];
|
|
2173
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2174
|
+
const normalized = [];
|
|
2175
|
+
for (const root of roots) {
|
|
2176
|
+
if (!root || typeof root !== "object") continue;
|
|
2177
|
+
const r = root;
|
|
2178
|
+
const uri = typeof r.uri === "string" ? r.uri : void 0;
|
|
2179
|
+
const name = typeof r.name === "string" ? r.name : void 0;
|
|
2180
|
+
if (!uri || seen.has(uri)) continue;
|
|
2181
|
+
seen.add(uri);
|
|
2182
|
+
normalized.push({ uri, name });
|
|
2248
2183
|
}
|
|
2249
|
-
return
|
|
2184
|
+
return normalized;
|
|
2250
2185
|
}
|
|
2251
|
-
function
|
|
2252
|
-
|
|
2253
|
-
if (haystack === needle) return 100;
|
|
2254
|
-
if (haystack.startsWith(needle)) return 75;
|
|
2255
|
-
if (haystack.includes(needle)) return 50;
|
|
2256
|
-
const compactNeedle = needle.replace(/[\s_-]+/g, "");
|
|
2257
|
-
const compactHaystack = haystack.replace(/[\s_-]+/g, "");
|
|
2258
|
-
if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
|
|
2259
|
-
return 0;
|
|
2186
|
+
function extractRootsFromResult(result) {
|
|
2187
|
+
return normalizeRoots(result?.roots);
|
|
2260
2188
|
}
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
name: "Repository Index",
|
|
2270
|
-
title: "Repository Index",
|
|
2271
|
-
description: "List of all known repositories with memory/task counts and last activity",
|
|
2272
|
-
mimeType: "application/json",
|
|
2273
|
-
annotations: {
|
|
2274
|
-
audience: ["assistant"],
|
|
2275
|
-
priority: 1,
|
|
2276
|
-
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
2277
|
-
}
|
|
2278
|
-
},
|
|
2279
|
-
{
|
|
2280
|
-
uri: "session://roots",
|
|
2281
|
-
name: "Session Roots",
|
|
2282
|
-
title: "Session Roots",
|
|
2283
|
-
description: session?.roots.length ? "Active workspace roots provided by the MCP client" : "No active workspace roots were provided by the MCP client",
|
|
2284
|
-
mimeType: "application/json",
|
|
2285
|
-
size: Buffer.byteLength(JSON.stringify({ roots: session?.roots ?? [] }), "utf8"),
|
|
2286
|
-
annotations: {
|
|
2287
|
-
audience: ["assistant"],
|
|
2288
|
-
priority: 0.95,
|
|
2289
|
-
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
2290
|
-
}
|
|
2189
|
+
function getFilesystemRoots(session) {
|
|
2190
|
+
if (!session) return [];
|
|
2191
|
+
const resolved = [];
|
|
2192
|
+
for (const root of session.roots) {
|
|
2193
|
+
if (!root.uri.startsWith("file://")) continue;
|
|
2194
|
+
try {
|
|
2195
|
+
resolved.push(path4.resolve(fileURLToPath2(root.uri)));
|
|
2196
|
+
} catch {
|
|
2291
2197
|
}
|
|
2292
|
-
|
|
2293
|
-
return
|
|
2198
|
+
}
|
|
2199
|
+
return resolved;
|
|
2294
2200
|
}
|
|
2295
|
-
function
|
|
2296
|
-
const
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
mimeType: "application/json",
|
|
2304
|
-
annotations: { audience: ["assistant"], priority: 0.85 }
|
|
2305
|
-
},
|
|
2306
|
-
{
|
|
2307
|
-
uriTemplate: "repository://{name}/memories?search={search}&type={type}&tag={tag}",
|
|
2308
|
-
name: "Filtered Repository Memories",
|
|
2309
|
-
title: "Filtered Repository Memories",
|
|
2310
|
-
description: "Filter or search memories within a repository by keyword, type, or tag",
|
|
2311
|
-
mimeType: "application/json",
|
|
2312
|
-
annotations: { audience: ["assistant"], priority: 0.8 }
|
|
2313
|
-
},
|
|
2314
|
-
{
|
|
2315
|
-
uriTemplate: "memory://{id}",
|
|
2316
|
-
name: "Memory Detail",
|
|
2317
|
-
title: "Memory Detail",
|
|
2318
|
-
description: "Full content and statistics for a specific memory UUID",
|
|
2319
|
-
mimeType: "application/json",
|
|
2320
|
-
annotations: { audience: ["assistant"], priority: 0.75 }
|
|
2321
|
-
},
|
|
2322
|
-
// ── Tasks ────────────────────────────────────────────────────────────────
|
|
2323
|
-
{
|
|
2324
|
-
uriTemplate: "repository://{name}/tasks",
|
|
2325
|
-
name: "Repository Tasks",
|
|
2326
|
-
title: "Repository Tasks",
|
|
2327
|
-
description: "All active tasks for a specific repository",
|
|
2328
|
-
mimeType: "application/json",
|
|
2329
|
-
annotations: { audience: ["assistant"], priority: 0.9 }
|
|
2330
|
-
},
|
|
2331
|
-
{
|
|
2332
|
-
uriTemplate: "repository://{name}/tasks?status={status}&priority={priority}",
|
|
2333
|
-
name: "Filtered Repository Tasks",
|
|
2334
|
-
title: "Filtered Repository Tasks",
|
|
2335
|
-
description: "Filter tasks within a repository by status or priority level",
|
|
2336
|
-
mimeType: "application/json",
|
|
2337
|
-
annotations: { audience: ["assistant"], priority: 0.85 }
|
|
2338
|
-
},
|
|
2339
|
-
{
|
|
2340
|
-
uriTemplate: "task://{id}",
|
|
2341
|
-
name: "Task Detail",
|
|
2342
|
-
title: "Task Detail",
|
|
2343
|
-
description: "Full content and comments for a specific task UUID",
|
|
2344
|
-
mimeType: "application/json",
|
|
2345
|
-
annotations: { audience: ["assistant"], priority: 0.8 }
|
|
2346
|
-
},
|
|
2347
|
-
// ── Repository extras ────────────────────────────────────────────────────
|
|
2348
|
-
{
|
|
2349
|
-
uriTemplate: "repository://{name}/summary",
|
|
2350
|
-
name: "Repository Summary",
|
|
2351
|
-
title: "Repository Summary",
|
|
2352
|
-
description: "High-level architectural summary for a repository",
|
|
2353
|
-
mimeType: "text/plain",
|
|
2354
|
-
annotations: { audience: ["assistant"], priority: 0.95 }
|
|
2355
|
-
},
|
|
2356
|
-
{
|
|
2357
|
-
uriTemplate: "repository://{name}/actions",
|
|
2358
|
-
name: "Repository Actions",
|
|
2359
|
-
title: "Repository Actions",
|
|
2360
|
-
description: "Audit log of agent tool actions scoped to a repository",
|
|
2361
|
-
mimeType: "application/json",
|
|
2362
|
-
annotations: { audience: ["assistant"], priority: 0.6 }
|
|
2363
|
-
},
|
|
2364
|
-
// ── Action detail ────────────────────────────────────────────────────────
|
|
2365
|
-
{
|
|
2366
|
-
uriTemplate: "action://{id}",
|
|
2367
|
-
name: "Action Detail",
|
|
2368
|
-
title: "Action Detail",
|
|
2369
|
-
description: "Full details of a specific audit log entry by integer ID",
|
|
2370
|
-
mimeType: "application/json",
|
|
2371
|
-
annotations: { audience: ["assistant"], priority: 0.55 }
|
|
2372
|
-
}
|
|
2373
|
-
];
|
|
2374
|
-
return paginateEntries("resourceTemplates", templates, params);
|
|
2201
|
+
function isPathWithinRoots(targetPath, session) {
|
|
2202
|
+
const roots = getFilesystemRoots(session);
|
|
2203
|
+
if (roots.length === 0) return true;
|
|
2204
|
+
const normalizedTarget = path4.resolve(targetPath);
|
|
2205
|
+
return roots.some((rootPath) => {
|
|
2206
|
+
const relative = path4.relative(rootPath, normalizedTarget);
|
|
2207
|
+
return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
|
|
2208
|
+
});
|
|
2375
2209
|
}
|
|
2376
|
-
function
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
return rankCompletionValues(dataSources.tags, argumentValue);
|
|
2210
|
+
function findContainingRoot(targetPath, session) {
|
|
2211
|
+
const roots = getFilesystemRoots(session);
|
|
2212
|
+
if (roots.length === 0) return null;
|
|
2213
|
+
const normalizedTarget = path4.resolve(targetPath);
|
|
2214
|
+
for (const rootPath of roots) {
|
|
2215
|
+
const relative = path4.relative(rootPath, normalizedTarget);
|
|
2216
|
+
if (relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative)) {
|
|
2217
|
+
return rootPath;
|
|
2385
2218
|
}
|
|
2386
2219
|
}
|
|
2387
|
-
|
|
2220
|
+
return null;
|
|
2388
2221
|
}
|
|
2389
|
-
function
|
|
2390
|
-
|
|
2391
|
-
if (
|
|
2392
|
-
|
|
2393
|
-
const payload = JSON.stringify(repos, null, 2);
|
|
2394
|
-
return {
|
|
2395
|
-
contents: [
|
|
2396
|
-
{
|
|
2397
|
-
uri,
|
|
2398
|
-
mimeType: "application/json",
|
|
2399
|
-
text: payload,
|
|
2400
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
2401
|
-
annotations: {
|
|
2402
|
-
audience: ["assistant"],
|
|
2403
|
-
priority: 1,
|
|
2404
|
-
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
]
|
|
2408
|
-
};
|
|
2409
|
-
}
|
|
2410
|
-
if (uri === "session://roots") {
|
|
2411
|
-
const payload = JSON.stringify({ roots: session?.roots ?? [] }, null, 2);
|
|
2412
|
-
return {
|
|
2413
|
-
contents: [
|
|
2414
|
-
{
|
|
2415
|
-
uri,
|
|
2416
|
-
mimeType: "application/json",
|
|
2417
|
-
text: payload,
|
|
2418
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
2419
|
-
annotations: {
|
|
2420
|
-
audience: ["assistant"],
|
|
2421
|
-
priority: 0.95,
|
|
2422
|
-
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
]
|
|
2426
|
-
};
|
|
2427
|
-
}
|
|
2428
|
-
const memoryIdMatch = uri.match(/^memory:\/\/([0-9a-f-]{36})$/i);
|
|
2429
|
-
if (memoryIdMatch) {
|
|
2430
|
-
const id = memoryIdMatch[1];
|
|
2431
|
-
const entry = db.memories.getByIdWithStats(id);
|
|
2432
|
-
if (!entry) throw resourceNotFound(`Memory with ID ${id} not found.`, uri);
|
|
2433
|
-
const payload = JSON.stringify(entry, null, 2);
|
|
2434
|
-
return {
|
|
2435
|
-
contents: [
|
|
2436
|
-
{
|
|
2437
|
-
uri,
|
|
2438
|
-
mimeType: "application/json",
|
|
2439
|
-
text: payload,
|
|
2440
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
2441
|
-
annotations: {
|
|
2442
|
-
audience: ["assistant"],
|
|
2443
|
-
priority: 0.75,
|
|
2444
|
-
lastModified: entry.updated_at || entry.created_at
|
|
2445
|
-
}
|
|
2446
|
-
}
|
|
2447
|
-
]
|
|
2448
|
-
};
|
|
2449
|
-
}
|
|
2450
|
-
const taskIdMatch = uri.match(/^task:\/\/([0-9a-f-]{36})$/i);
|
|
2451
|
-
if (taskIdMatch) {
|
|
2452
|
-
const id = taskIdMatch[1];
|
|
2453
|
-
const task = db.tasks.getTaskById(id);
|
|
2454
|
-
if (!task) throw resourceNotFound(`Task with ID ${id} not found.`, uri);
|
|
2455
|
-
const payload = JSON.stringify(task, null, 2);
|
|
2456
|
-
return {
|
|
2457
|
-
contents: [
|
|
2458
|
-
{
|
|
2459
|
-
uri,
|
|
2460
|
-
mimeType: "application/json",
|
|
2461
|
-
text: payload,
|
|
2462
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
2463
|
-
annotations: {
|
|
2464
|
-
audience: ["assistant"],
|
|
2465
|
-
priority: 0.8,
|
|
2466
|
-
lastModified: task.updated_at || task.created_at
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
]
|
|
2470
|
-
};
|
|
2222
|
+
function inferRepoFromSession(session) {
|
|
2223
|
+
const roots = getFilesystemRoots(session);
|
|
2224
|
+
if (roots.length === 1) {
|
|
2225
|
+
return path4.basename(roots[0]);
|
|
2471
2226
|
}
|
|
2472
|
-
|
|
2473
|
-
if (repoBase) {
|
|
2474
|
-
const { name, path: repoPath, query } = repoBase;
|
|
2475
|
-
if (repoPath === "summary") {
|
|
2476
|
-
const summary = db.summaries.getSummary(name);
|
|
2477
|
-
const text = summary?.summary || `No summary available for repository: ${name}`;
|
|
2478
|
-
return {
|
|
2479
|
-
contents: [
|
|
2480
|
-
{
|
|
2481
|
-
uri,
|
|
2482
|
-
mimeType: "text/plain",
|
|
2483
|
-
text,
|
|
2484
|
-
size: Buffer.byteLength(text, "utf8"),
|
|
2485
|
-
annotations: {
|
|
2486
|
-
audience: ["assistant"],
|
|
2487
|
-
priority: 0.95,
|
|
2488
|
-
lastModified: summary?.updated_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
2489
|
-
}
|
|
2490
|
-
}
|
|
2491
|
-
]
|
|
2492
|
-
};
|
|
2493
|
-
}
|
|
2494
|
-
if (repoPath === "memories") {
|
|
2495
|
-
const search = query.get("search") || "";
|
|
2496
|
-
const type = query.get("type");
|
|
2497
|
-
const tag = query.get("tag");
|
|
2498
|
-
const result = db.memories.listMemoriesForDashboard({
|
|
2499
|
-
repo: name,
|
|
2500
|
-
type: type || void 0,
|
|
2501
|
-
tag: tag || void 0,
|
|
2502
|
-
search: search || void 0,
|
|
2503
|
-
limit: 50
|
|
2504
|
-
});
|
|
2505
|
-
const entries = result.items;
|
|
2506
|
-
const payload = JSON.stringify(entries, null, 2);
|
|
2507
|
-
return {
|
|
2508
|
-
contents: [
|
|
2509
|
-
{
|
|
2510
|
-
uri,
|
|
2511
|
-
mimeType: "application/json",
|
|
2512
|
-
text: payload,
|
|
2513
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
2514
|
-
annotations: {
|
|
2515
|
-
audience: ["assistant"],
|
|
2516
|
-
priority: 0.85,
|
|
2517
|
-
lastModified: deriveLastModifiedFromCollection(
|
|
2518
|
-
entries.map((e) => e.updated_at || e.created_at)
|
|
2519
|
-
)
|
|
2520
|
-
}
|
|
2521
|
-
}
|
|
2522
|
-
]
|
|
2523
|
-
};
|
|
2524
|
-
}
|
|
2525
|
-
if (repoPath === "tasks") {
|
|
2526
|
-
const status = query.get("status");
|
|
2527
|
-
const priority = query.get("priority");
|
|
2528
|
-
let tasks;
|
|
2529
|
-
if (status && status !== "all") {
|
|
2530
|
-
const statuses = status.split(",").map((s) => s.trim());
|
|
2531
|
-
tasks = db.tasks.getTasksByMultipleStatuses(name, statuses);
|
|
2532
|
-
} else {
|
|
2533
|
-
tasks = db.tasks.getTasksByMultipleStatuses(name, ["backlog", "pending", "in_progress", "blocked"]);
|
|
2534
|
-
}
|
|
2535
|
-
if (priority) {
|
|
2536
|
-
const p = Number(priority);
|
|
2537
|
-
if (!isNaN(p)) {
|
|
2538
|
-
tasks = tasks.filter((t) => t.priority === p);
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
const payload = JSON.stringify(tasks, null, 2);
|
|
2542
|
-
return {
|
|
2543
|
-
contents: [
|
|
2544
|
-
{
|
|
2545
|
-
uri,
|
|
2546
|
-
mimeType: "application/json",
|
|
2547
|
-
text: payload,
|
|
2548
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
2549
|
-
annotations: {
|
|
2550
|
-
audience: ["assistant"],
|
|
2551
|
-
priority: 0.9,
|
|
2552
|
-
lastModified: deriveLastModifiedFromCollection(tasks.map((t) => t.updated_at))
|
|
2553
|
-
}
|
|
2554
|
-
}
|
|
2555
|
-
]
|
|
2556
|
-
};
|
|
2557
|
-
}
|
|
2558
|
-
if (repoPath === "actions") {
|
|
2559
|
-
const actions = db.actions.getRecentActions(name, 100);
|
|
2560
|
-
const payload = JSON.stringify(actions, null, 2);
|
|
2561
|
-
return {
|
|
2562
|
-
contents: [
|
|
2563
|
-
{
|
|
2564
|
-
uri,
|
|
2565
|
-
mimeType: "application/json",
|
|
2566
|
-
text: payload,
|
|
2567
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
2568
|
-
annotations: {
|
|
2569
|
-
audience: ["assistant"],
|
|
2570
|
-
priority: 0.6,
|
|
2571
|
-
lastModified: deriveLastModifiedFromCollection(actions.map((a) => a.created_at))
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
|
-
]
|
|
2575
|
-
};
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
const actionIdMatch = uri.match(/^action:\/\/(\d+)$/);
|
|
2579
|
-
if (actionIdMatch) {
|
|
2580
|
-
const id = Number(actionIdMatch[1]);
|
|
2581
|
-
const action = db.actions.getActionById(id);
|
|
2582
|
-
if (!action) throw resourceNotFound(`Action with ID ${id} not found.`, uri);
|
|
2583
|
-
const payload = JSON.stringify(action, null, 2);
|
|
2584
|
-
return {
|
|
2585
|
-
contents: [
|
|
2586
|
-
{
|
|
2587
|
-
uri,
|
|
2588
|
-
mimeType: "application/json",
|
|
2589
|
-
text: payload,
|
|
2590
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
2591
|
-
annotations: {
|
|
2592
|
-
audience: ["assistant"],
|
|
2593
|
-
priority: 0.55,
|
|
2594
|
-
lastModified: action.created_at
|
|
2595
|
-
}
|
|
2596
|
-
}
|
|
2597
|
-
]
|
|
2598
|
-
};
|
|
2599
|
-
}
|
|
2600
|
-
throw resourceNotFound(`Unknown resource URI: ${uri}`, uri);
|
|
2601
|
-
}
|
|
2602
|
-
function parseRepoUri(uri) {
|
|
2603
|
-
const prefix = "repository://";
|
|
2604
|
-
if (!uri.startsWith(prefix)) return null;
|
|
2605
|
-
const rest = uri.slice(prefix.length);
|
|
2606
|
-
const queryStart = rest.indexOf("?");
|
|
2607
|
-
const withoutQuery = queryStart === -1 ? rest : rest.slice(0, queryStart);
|
|
2608
|
-
const queryString = queryStart === -1 ? "" : rest.slice(queryStart + 1);
|
|
2609
|
-
const slashIdx = withoutQuery.indexOf("/");
|
|
2610
|
-
if (slashIdx === -1) return null;
|
|
2611
|
-
const name = withoutQuery.slice(0, slashIdx);
|
|
2612
|
-
const path6 = withoutQuery.slice(slashIdx + 1);
|
|
2613
|
-
if (!name || !path6) return null;
|
|
2614
|
-
return { name, path: path6, query: new URLSearchParams(queryString) };
|
|
2615
|
-
}
|
|
2616
|
-
function paginateEntries(key, entries, params) {
|
|
2617
|
-
const limit = normalizeLimit(params?.limit);
|
|
2618
|
-
const offset = decodeCursor(params?.cursor);
|
|
2619
|
-
const sliced = entries.slice(offset, offset + limit);
|
|
2620
|
-
const nextOffset = offset + sliced.length;
|
|
2621
|
-
return {
|
|
2622
|
-
[key]: sliced,
|
|
2623
|
-
nextCursor: nextOffset < entries.length ? encodeCursor(nextOffset) : void 0
|
|
2624
|
-
};
|
|
2625
|
-
}
|
|
2626
|
-
function normalizeLimit(limit) {
|
|
2627
|
-
if (typeof limit !== "number" || !Number.isFinite(limit)) {
|
|
2628
|
-
return DEFAULT_PAGE_SIZE;
|
|
2629
|
-
}
|
|
2630
|
-
return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
|
|
2631
|
-
}
|
|
2632
|
-
function deriveLastModifiedFromCollection(values) {
|
|
2633
|
-
const normalized = values.filter((value) => typeof value === "string" && value.length > 0);
|
|
2634
|
-
return normalized.sort().at(-1) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2635
|
-
}
|
|
2636
|
-
function resourceNotFound(message, uri) {
|
|
2637
|
-
const error = new Error(message);
|
|
2638
|
-
error.code = -32002;
|
|
2639
|
-
error.data = { uri };
|
|
2640
|
-
return error;
|
|
2641
|
-
}
|
|
2642
|
-
function invalidCompletionParams(message) {
|
|
2643
|
-
const error = new Error(message);
|
|
2644
|
-
error.code = -32602;
|
|
2645
|
-
return error;
|
|
2646
|
-
}
|
|
2647
|
-
|
|
2648
|
-
// src/mcp/prompts/loader.ts
|
|
2649
|
-
import fs4 from "fs";
|
|
2650
|
-
import path5 from "path";
|
|
2651
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2652
|
-
import matter from "gray-matter";
|
|
2653
|
-
var __filename = fileURLToPath3(import.meta.url);
|
|
2654
|
-
var __dirname2 = path5.dirname(__filename);
|
|
2655
|
-
function findPromptDir() {
|
|
2656
|
-
const candidates = [
|
|
2657
|
-
// Production if chunked into dist/
|
|
2658
|
-
"./prompts",
|
|
2659
|
-
// Production if inlined into dist/mcp/
|
|
2660
|
-
"../prompts",
|
|
2661
|
-
// Dev: /src/mcp/prompts/definitions (next to loader.ts)
|
|
2662
|
-
"./definitions"
|
|
2663
|
-
].map((relPath) => path5.resolve(__dirname2, relPath));
|
|
2664
|
-
for (const dir of candidates) {
|
|
2665
|
-
if (fs4.existsSync(dir)) {
|
|
2666
|
-
const files = fs4.readdirSync(dir);
|
|
2667
|
-
if (files.some((f) => f.endsWith(".md"))) {
|
|
2668
|
-
return dir;
|
|
2669
|
-
}
|
|
2670
|
-
}
|
|
2671
|
-
}
|
|
2672
|
-
return path5.resolve(__dirname2, "./definitions");
|
|
2673
|
-
}
|
|
2674
|
-
var PROMPT_DIR = findPromptDir();
|
|
2675
|
-
function listPromptFiles() {
|
|
2676
|
-
if (!fs4.existsSync(PROMPT_DIR)) return [];
|
|
2677
|
-
return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
|
|
2678
|
-
}
|
|
2679
|
-
function loadPromptFromMarkdown(name) {
|
|
2680
|
-
const filePath = path5.join(PROMPT_DIR, `${name}.md`);
|
|
2681
|
-
if (!fs4.existsSync(filePath)) {
|
|
2682
|
-
throw new Error(`Prompt file not found: ${filePath}`);
|
|
2683
|
-
}
|
|
2684
|
-
const fileContent = fs4.readFileSync(filePath, "utf-8");
|
|
2685
|
-
const { data, content } = matter(fileContent);
|
|
2686
|
-
return {
|
|
2687
|
-
name: data.name || name,
|
|
2688
|
-
description: data.description || "",
|
|
2689
|
-
arguments: data.arguments || [],
|
|
2690
|
-
agent: data.agent,
|
|
2691
|
-
content: content.trim()
|
|
2692
|
-
};
|
|
2693
|
-
}
|
|
2694
|
-
|
|
2695
|
-
// src/mcp/prompts/registry.ts
|
|
2696
|
-
function createPromptDefinition(loaded) {
|
|
2697
|
-
return {
|
|
2698
|
-
name: loaded.name,
|
|
2699
|
-
description: loaded.description,
|
|
2700
|
-
arguments: loaded.arguments,
|
|
2701
|
-
agent: loaded.agent,
|
|
2702
|
-
messages: [
|
|
2703
|
-
{
|
|
2704
|
-
role: "user",
|
|
2705
|
-
content: {
|
|
2706
|
-
type: "text",
|
|
2707
|
-
text: loaded.content
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2710
|
-
]
|
|
2711
|
-
};
|
|
2712
|
-
}
|
|
2713
|
-
var PROMPTS = {};
|
|
2714
|
-
var promptFiles = listPromptFiles();
|
|
2715
|
-
for (const name of promptFiles) {
|
|
2716
|
-
try {
|
|
2717
|
-
PROMPTS[name] = createPromptDefinition(loadPromptFromMarkdown(name));
|
|
2718
|
-
} catch (e) {
|
|
2719
|
-
console.warn(`Failed to load prompt ${name}: ${e}`);
|
|
2720
|
-
}
|
|
2721
|
-
}
|
|
2722
|
-
async function listPrompts(db, session, params) {
|
|
2723
|
-
const allPrompts = Object.values(PROMPTS).map((p) => ({
|
|
2724
|
-
name: p.name,
|
|
2725
|
-
description: p.description,
|
|
2726
|
-
arguments: p.arguments,
|
|
2727
|
-
metadata: p.agent ? { agent: p.agent } : void 0
|
|
2728
|
-
}));
|
|
2729
|
-
const rawLimit = typeof params?.limit === "number" && Number.isInteger(params?.limit) ? params.limit : 25;
|
|
2730
|
-
const limit = Math.max(1, Math.min(100, Math.trunc(rawLimit)));
|
|
2731
|
-
const offset = decodeCursor(params?.cursor);
|
|
2732
|
-
const sliced = allPrompts.slice(offset, offset + limit);
|
|
2733
|
-
const nextOffset = offset + sliced.length;
|
|
2734
|
-
return {
|
|
2735
|
-
prompts: sliced,
|
|
2736
|
-
nextCursor: nextOffset < allPrompts.length ? encodeCursor(nextOffset) : void 0
|
|
2737
|
-
};
|
|
2738
|
-
}
|
|
2739
|
-
async function getPrompt(name, args = {}, db, session) {
|
|
2740
|
-
const prompt = PROMPTS[name];
|
|
2741
|
-
if (!prompt) {
|
|
2742
|
-
throw new Error(`Prompt not found: ${name}`);
|
|
2743
|
-
}
|
|
2744
|
-
const inferredRepo = inferRepoFromSession(session);
|
|
2745
|
-
const messages = prompt.messages.map((m) => {
|
|
2746
|
-
let text = m.content.text;
|
|
2747
|
-
for (const [key, value] of Object.entries(args)) {
|
|
2748
|
-
text = text.replace(new RegExp(`\\{{${key}\\}}`, "g"), value);
|
|
2749
|
-
}
|
|
2750
|
-
text = text.replace(/{{current_repo}}/g, inferredRepo || "unknown-repo");
|
|
2751
|
-
return {
|
|
2752
|
-
...m,
|
|
2753
|
-
content: {
|
|
2754
|
-
...m.content,
|
|
2755
|
-
text
|
|
2756
|
-
}
|
|
2757
|
-
};
|
|
2758
|
-
});
|
|
2759
|
-
return {
|
|
2760
|
-
description: prompt.description,
|
|
2761
|
-
messages,
|
|
2762
|
-
metadata: prompt.agent ? { agent: prompt.agent } : void 0
|
|
2763
|
-
};
|
|
2764
|
-
}
|
|
2765
|
-
async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
|
|
2766
|
-
void name;
|
|
2767
|
-
void contextArguments;
|
|
2768
|
-
if (argName === "task_id") {
|
|
2769
|
-
const values = dataSources.tasks.map((t) => t.id);
|
|
2770
|
-
return rankCompletionValues(values, value);
|
|
2771
|
-
}
|
|
2772
|
-
return [];
|
|
2227
|
+
return void 0;
|
|
2773
2228
|
}
|
|
2774
2229
|
|
|
2775
2230
|
// src/mcp/tools/schemas.ts
|
|
@@ -2995,828 +2450,1380 @@ var TaskGetSchema = z.object({
|
|
|
2995
2450
|
});
|
|
2996
2451
|
var TOOL_DEFINITIONS = [
|
|
2997
2452
|
{
|
|
2998
|
-
name: "memory-synthesize",
|
|
2999
|
-
title: "Memory Synthesize",
|
|
3000
|
-
description: "Use client sampling to synthesize a grounded answer from local memory and tasks. Best for project briefings, tradeoff summaries, and context-aware answers.",
|
|
2453
|
+
name: "memory-synthesize",
|
|
2454
|
+
title: "Memory Synthesize",
|
|
2455
|
+
description: "Use client sampling to synthesize a grounded answer from local memory and tasks. Best for project briefings, tradeoff summaries, and context-aware answers.",
|
|
2456
|
+
annotations: {
|
|
2457
|
+
readOnlyHint: true,
|
|
2458
|
+
idempotentHint: true,
|
|
2459
|
+
openWorldHint: false
|
|
2460
|
+
},
|
|
2461
|
+
execution: {
|
|
2462
|
+
taskSupport: "optional"
|
|
2463
|
+
},
|
|
2464
|
+
inputSchema: {
|
|
2465
|
+
type: "object",
|
|
2466
|
+
properties: {
|
|
2467
|
+
repo: { type: "string", description: "Repository name. Optional when a single MCP root is active." },
|
|
2468
|
+
objective: { type: "string", minLength: 5, description: "Question or synthesis objective." },
|
|
2469
|
+
current_file_path: {
|
|
2470
|
+
type: "string",
|
|
2471
|
+
description: "Optional absolute file path for workspace-local grounding."
|
|
2472
|
+
},
|
|
2473
|
+
include_summary: { type: "boolean", default: true },
|
|
2474
|
+
include_tasks: { type: "boolean", default: true },
|
|
2475
|
+
use_tools: {
|
|
2476
|
+
type: "boolean",
|
|
2477
|
+
default: true,
|
|
2478
|
+
description: "Allow the sampled model to call local memory/task tools during synthesis when the client supports sampling.tools."
|
|
2479
|
+
},
|
|
2480
|
+
max_iterations: { type: "number", minimum: 1, maximum: 5, default: 3 },
|
|
2481
|
+
max_tokens: { type: "number", minimum: 128, maximum: 4e3, default: 1200 },
|
|
2482
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON results." }
|
|
2483
|
+
},
|
|
2484
|
+
required: ["objective"]
|
|
2485
|
+
},
|
|
2486
|
+
outputSchema: {
|
|
2487
|
+
type: "object",
|
|
2488
|
+
properties: {
|
|
2489
|
+
repo: { type: "string" },
|
|
2490
|
+
objective: { type: "string" },
|
|
2491
|
+
answer: { type: "string" },
|
|
2492
|
+
model: { type: "string" },
|
|
2493
|
+
stopReason: { type: "string" },
|
|
2494
|
+
iterations: { type: "number" },
|
|
2495
|
+
toolCalls: { type: "number" }
|
|
2496
|
+
},
|
|
2497
|
+
required: ["repo", "objective", "answer", "iterations", "toolCalls"]
|
|
2498
|
+
}
|
|
2499
|
+
},
|
|
2500
|
+
{
|
|
2501
|
+
name: "task-create-interactive",
|
|
2502
|
+
title: "Interactive Task Create",
|
|
2503
|
+
description: "Create a task with MCP elicitation fallback for any missing required fields. Best when an agent knows a task is needed but still needs user confirmation for repo, title, or phase.",
|
|
2504
|
+
annotations: {
|
|
2505
|
+
readOnlyHint: false,
|
|
2506
|
+
idempotentHint: false,
|
|
2507
|
+
destructiveHint: false,
|
|
2508
|
+
openWorldHint: false
|
|
2509
|
+
},
|
|
2510
|
+
inputSchema: {
|
|
2511
|
+
type: "object",
|
|
2512
|
+
properties: {
|
|
2513
|
+
repo: {
|
|
2514
|
+
type: "string",
|
|
2515
|
+
description: "Repository name. Optional when it can be inferred from MCP roots or elicited from the user."
|
|
2516
|
+
},
|
|
2517
|
+
task_code: { type: "string" },
|
|
2518
|
+
phase: { type: "string" },
|
|
2519
|
+
title: { type: "string", minLength: 3, maxLength: 100 },
|
|
2520
|
+
description: { type: "string", minLength: 1 },
|
|
2521
|
+
status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
|
|
2522
|
+
priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
|
|
2523
|
+
agent: { type: "string" },
|
|
2524
|
+
role: { type: "string" },
|
|
2525
|
+
doc_path: { type: "string" },
|
|
2526
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
2527
|
+
}
|
|
2528
|
+
},
|
|
2529
|
+
outputSchema: {
|
|
2530
|
+
type: "object",
|
|
2531
|
+
properties: {
|
|
2532
|
+
repo: { type: "string" },
|
|
2533
|
+
task_code: { type: "string" },
|
|
2534
|
+
phase: { type: "string" },
|
|
2535
|
+
title: { type: "string" },
|
|
2536
|
+
status: { type: "string" },
|
|
2537
|
+
priority: { type: "number" }
|
|
2538
|
+
},
|
|
2539
|
+
required: ["repo", "task_code", "phase", "title", "status", "priority"]
|
|
2540
|
+
}
|
|
2541
|
+
},
|
|
2542
|
+
{
|
|
2543
|
+
name: "memory-detail",
|
|
2544
|
+
title: "Memory Detail",
|
|
2545
|
+
description: "Fetch full details of a specific memory by ID. Use this when you have a memory ID (e.g. from search results) and need to read the full content.",
|
|
2546
|
+
inputSchema: {
|
|
2547
|
+
type: "object",
|
|
2548
|
+
properties: {
|
|
2549
|
+
id: { type: "string", format: "uuid", description: "Memory entry ID" },
|
|
2550
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
|
|
2551
|
+
},
|
|
2552
|
+
required: ["id"]
|
|
2553
|
+
}
|
|
2554
|
+
},
|
|
2555
|
+
{
|
|
2556
|
+
name: "task-detail",
|
|
2557
|
+
title: "Task Detail",
|
|
2558
|
+
description: "Fetch full details of a specific task by ID or task code. Use this when you have a task ID or code and need to read the full description and comments.",
|
|
2559
|
+
inputSchema: {
|
|
2560
|
+
type: "object",
|
|
2561
|
+
properties: {
|
|
2562
|
+
repo: { type: "string", description: "Repository name" },
|
|
2563
|
+
id: { type: "string", format: "uuid", description: "Task ID (optional if task_code is provided)" },
|
|
2564
|
+
task_code: { type: "string", description: "Task code (e.g. TASK-001) (optional if id is provided)" },
|
|
2565
|
+
structured: {
|
|
2566
|
+
type: "boolean",
|
|
2567
|
+
default: false,
|
|
2568
|
+
description: "If true, returns structured JSON without the text content details."
|
|
2569
|
+
}
|
|
2570
|
+
},
|
|
2571
|
+
required: ["repo"]
|
|
2572
|
+
}
|
|
2573
|
+
},
|
|
2574
|
+
{
|
|
2575
|
+
name: "memory-store",
|
|
2576
|
+
title: "Memory Store",
|
|
2577
|
+
description: "Store a new memory entry. Keep 'title' concise and human-readable; do not embed agent/role/date metadata in the title. Put auxiliary context into 'metadata'. Use 'tags' for tech-stack and 'is_global' for universal rules.",
|
|
2578
|
+
annotations: {
|
|
2579
|
+
readOnlyHint: false,
|
|
2580
|
+
idempotentHint: false,
|
|
2581
|
+
destructiveHint: false,
|
|
2582
|
+
openWorldHint: false
|
|
2583
|
+
},
|
|
2584
|
+
inputSchema: {
|
|
2585
|
+
type: "object",
|
|
2586
|
+
properties: {
|
|
2587
|
+
type: {
|
|
2588
|
+
type: "string",
|
|
2589
|
+
enum: [
|
|
2590
|
+
"code_fact",
|
|
2591
|
+
"decision",
|
|
2592
|
+
"mistake",
|
|
2593
|
+
"pattern",
|
|
2594
|
+
"agent_handoff",
|
|
2595
|
+
"agent_registered",
|
|
2596
|
+
"file_claim",
|
|
2597
|
+
"task_archive"
|
|
2598
|
+
],
|
|
2599
|
+
description: "Type of memory being stored"
|
|
2600
|
+
},
|
|
2601
|
+
title: {
|
|
2602
|
+
type: "string",
|
|
2603
|
+
minLength: 3,
|
|
2604
|
+
maxLength: 100,
|
|
2605
|
+
description: "Short human-readable title for the memory. Do not embed bracketed metadata like agent/role/date prefixes here."
|
|
2606
|
+
},
|
|
2607
|
+
content: {
|
|
2608
|
+
type: "string",
|
|
2609
|
+
minLength: 10,
|
|
2610
|
+
description: "The memory content"
|
|
2611
|
+
},
|
|
2612
|
+
importance: {
|
|
2613
|
+
type: "number",
|
|
2614
|
+
minimum: 1,
|
|
2615
|
+
maximum: 5,
|
|
2616
|
+
description: "Importance score (1-5)"
|
|
2617
|
+
},
|
|
2618
|
+
agent: {
|
|
2619
|
+
type: "string",
|
|
2620
|
+
description: "Name of the agent creating this memory"
|
|
2621
|
+
},
|
|
2622
|
+
role: {
|
|
2623
|
+
type: "string",
|
|
2624
|
+
description: "Role of the agent creating this memory"
|
|
2625
|
+
},
|
|
2626
|
+
model: {
|
|
2627
|
+
type: "string",
|
|
2628
|
+
description: "AI model used by the agent"
|
|
2629
|
+
},
|
|
2630
|
+
scope: {
|
|
2631
|
+
type: "object",
|
|
2632
|
+
properties: {
|
|
2633
|
+
repo: { type: "string", description: "Repository name" },
|
|
2634
|
+
branch: { type: "string" },
|
|
2635
|
+
folder: { type: "string" },
|
|
2636
|
+
language: { type: "string" }
|
|
2637
|
+
},
|
|
2638
|
+
required: ["repo"]
|
|
2639
|
+
},
|
|
2640
|
+
tags: {
|
|
2641
|
+
type: "array",
|
|
2642
|
+
items: { type: "string" },
|
|
2643
|
+
description: "Technology stack tags (e.g., ['filament', 'laravel'])"
|
|
2644
|
+
},
|
|
2645
|
+
metadata: {
|
|
2646
|
+
type: "object",
|
|
2647
|
+
description: "Structured metadata for non-title context such as source agent, claim fields, or timestamps"
|
|
2648
|
+
},
|
|
2649
|
+
is_global: {
|
|
2650
|
+
type: "boolean",
|
|
2651
|
+
description: "If true, this memory is shared across all repositories"
|
|
2652
|
+
},
|
|
2653
|
+
ttlDays: { type: "number", minimum: 1 },
|
|
2654
|
+
supersedes: { type: "string", format: "uuid" },
|
|
2655
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the stored memory." }
|
|
2656
|
+
},
|
|
2657
|
+
required: ["type", "title", "content", "importance", "scope", "agent", "model"]
|
|
2658
|
+
},
|
|
2659
|
+
outputSchema: {
|
|
2660
|
+
type: "object",
|
|
2661
|
+
properties: {
|
|
2662
|
+
success: { type: "boolean" },
|
|
2663
|
+
id: { type: "string" },
|
|
2664
|
+
code: { type: "string" },
|
|
2665
|
+
repo: { type: "string" },
|
|
2666
|
+
type: { type: "string" },
|
|
2667
|
+
title: { type: "string" },
|
|
2668
|
+
error: { type: "string" },
|
|
2669
|
+
message: { type: "string" }
|
|
2670
|
+
},
|
|
2671
|
+
required: ["success"]
|
|
2672
|
+
}
|
|
2673
|
+
},
|
|
2674
|
+
{
|
|
2675
|
+
name: "memory-acknowledge",
|
|
2676
|
+
title: "Memory Acknowledge",
|
|
2677
|
+
description: "Acknowledge the use of a memory or report its irrelevance/contradiction. Mandatory after using memory to generate code.",
|
|
2678
|
+
annotations: {
|
|
2679
|
+
readOnlyHint: false,
|
|
2680
|
+
idempotentHint: false,
|
|
2681
|
+
openWorldHint: false
|
|
2682
|
+
},
|
|
2683
|
+
inputSchema: {
|
|
2684
|
+
type: "object",
|
|
2685
|
+
properties: {
|
|
2686
|
+
memory_id: { type: "string", format: "uuid" },
|
|
2687
|
+
status: { type: "string", enum: ["used", "irrelevant", "contradictory"] },
|
|
2688
|
+
application_context: { type: "string", minLength: 10 },
|
|
2689
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
2690
|
+
},
|
|
2691
|
+
required: ["memory_id", "status"]
|
|
2692
|
+
},
|
|
2693
|
+
outputSchema: {
|
|
2694
|
+
type: "object",
|
|
2695
|
+
properties: {
|
|
2696
|
+
success: { type: "boolean" },
|
|
2697
|
+
id: { type: "string" },
|
|
2698
|
+
status: { type: "string" }
|
|
2699
|
+
},
|
|
2700
|
+
required: ["success", "id", "status"]
|
|
2701
|
+
}
|
|
2702
|
+
},
|
|
2703
|
+
{
|
|
2704
|
+
name: "memory-update",
|
|
2705
|
+
title: "Memory Update",
|
|
2706
|
+
description: "Update an existing memory entry. Keep 'title' concise and move agent/role/date or claim context into 'metadata' instead of the title.",
|
|
2707
|
+
annotations: {
|
|
2708
|
+
readOnlyHint: false,
|
|
2709
|
+
idempotentHint: false,
|
|
2710
|
+
destructiveHint: false,
|
|
2711
|
+
openWorldHint: false
|
|
2712
|
+
},
|
|
2713
|
+
inputSchema: {
|
|
2714
|
+
type: "object",
|
|
2715
|
+
properties: {
|
|
2716
|
+
id: { type: "string", format: "uuid" },
|
|
2717
|
+
type: {
|
|
2718
|
+
type: "string",
|
|
2719
|
+
enum: [
|
|
2720
|
+
"code_fact",
|
|
2721
|
+
"decision",
|
|
2722
|
+
"mistake",
|
|
2723
|
+
"pattern",
|
|
2724
|
+
"agent_handoff",
|
|
2725
|
+
"agent_registered",
|
|
2726
|
+
"file_claim",
|
|
2727
|
+
"task_archive"
|
|
2728
|
+
]
|
|
2729
|
+
},
|
|
2730
|
+
title: { type: "string", minLength: 3, maxLength: 100 },
|
|
2731
|
+
content: { type: "string", minLength: 10 },
|
|
2732
|
+
importance: { type: "number", minimum: 1, maximum: 5 },
|
|
2733
|
+
agent: { type: "string" },
|
|
2734
|
+
role: { type: "string" },
|
|
2735
|
+
status: { type: "string", enum: ["active", "archived"] },
|
|
2736
|
+
supersedes: { type: "string", format: "uuid" },
|
|
2737
|
+
tags: { type: "array", items: { type: "string" } },
|
|
2738
|
+
metadata: { type: "object" },
|
|
2739
|
+
is_global: { type: "boolean" },
|
|
2740
|
+
completed_at: { type: "string" },
|
|
2741
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the updated memory." }
|
|
2742
|
+
},
|
|
2743
|
+
required: ["id"]
|
|
2744
|
+
},
|
|
2745
|
+
outputSchema: {
|
|
2746
|
+
type: "object",
|
|
2747
|
+
properties: {
|
|
2748
|
+
success: { type: "boolean" },
|
|
2749
|
+
id: { type: "string" },
|
|
2750
|
+
repo: { type: "string" },
|
|
2751
|
+
updatedFields: {
|
|
2752
|
+
type: "array",
|
|
2753
|
+
items: { type: "string" }
|
|
2754
|
+
}
|
|
2755
|
+
},
|
|
2756
|
+
required: ["success", "id", "repo", "updatedFields"]
|
|
2757
|
+
}
|
|
2758
|
+
},
|
|
2759
|
+
{
|
|
2760
|
+
name: "memory-search",
|
|
2761
|
+
title: "Memory Search",
|
|
2762
|
+
description: "NAVIGATION LAYER: Returns a pointer table of matching memory IDs only. Returns columns [id, title, type, importance] \u2014 NO content. Retrieve full memory via memory-detail. Use 'current_tags' to find tech-stack specific knowledge from other projects.",
|
|
2763
|
+
annotations: {
|
|
2764
|
+
readOnlyHint: true,
|
|
2765
|
+
idempotentHint: true,
|
|
2766
|
+
openWorldHint: false
|
|
2767
|
+
},
|
|
2768
|
+
inputSchema: {
|
|
2769
|
+
type: "object",
|
|
2770
|
+
properties: {
|
|
2771
|
+
query: { type: "string", minLength: 3 },
|
|
2772
|
+
prompt: { type: "string" },
|
|
2773
|
+
repo: { type: "string" },
|
|
2774
|
+
current_tags: {
|
|
2775
|
+
type: "array",
|
|
2776
|
+
items: { type: "string" },
|
|
2777
|
+
description: "Active tech stack tags (e.g., ['filament', 'react'])"
|
|
2778
|
+
},
|
|
2779
|
+
types: {
|
|
2780
|
+
type: "array",
|
|
2781
|
+
items: {
|
|
2782
|
+
type: "string",
|
|
2783
|
+
enum: [
|
|
2784
|
+
"code_fact",
|
|
2785
|
+
"decision",
|
|
2786
|
+
"mistake",
|
|
2787
|
+
"pattern",
|
|
2788
|
+
"agent_handoff",
|
|
2789
|
+
"agent_registered",
|
|
2790
|
+
"file_claim",
|
|
2791
|
+
"task_archive"
|
|
2792
|
+
]
|
|
2793
|
+
}
|
|
2794
|
+
},
|
|
2795
|
+
minImportance: { type: "number", minimum: 1, maximum: 5 },
|
|
2796
|
+
limit: { type: "number", minimum: 1, maximum: 100, default: 5 },
|
|
2797
|
+
offset: { type: "number", minimum: 0, default: 0 },
|
|
2798
|
+
includeRecap: { type: "boolean", default: false },
|
|
2799
|
+
current_file_path: { type: "string" },
|
|
2800
|
+
include_archived: { type: "boolean", default: false },
|
|
2801
|
+
scope: {
|
|
2802
|
+
type: "object",
|
|
2803
|
+
properties: {
|
|
2804
|
+
repo: { type: "string" },
|
|
2805
|
+
branch: { type: "string" },
|
|
2806
|
+
folder: { type: "string" },
|
|
2807
|
+
language: { type: "string" }
|
|
2808
|
+
}
|
|
2809
|
+
},
|
|
2810
|
+
structured: {
|
|
2811
|
+
type: "boolean",
|
|
2812
|
+
default: false,
|
|
2813
|
+
description: "If true, returns structured JSON without the text content summary."
|
|
2814
|
+
}
|
|
2815
|
+
},
|
|
2816
|
+
required: ["query", "repo"]
|
|
2817
|
+
},
|
|
2818
|
+
outputSchema: {
|
|
2819
|
+
type: "object",
|
|
2820
|
+
properties: {
|
|
2821
|
+
schema: { type: "string", enum: ["memory-search"] },
|
|
2822
|
+
query: { type: "string" },
|
|
2823
|
+
count: { type: "number", description: "Number of rows returned" },
|
|
2824
|
+
total: { type: "number", description: "Total matching memories" },
|
|
2825
|
+
offset: { type: "number" },
|
|
2826
|
+
limit: { type: "number" },
|
|
2827
|
+
results: {
|
|
2828
|
+
type: "object",
|
|
2829
|
+
properties: {
|
|
2830
|
+
columns: {
|
|
2831
|
+
type: "array",
|
|
2832
|
+
items: { type: "string" },
|
|
2833
|
+
description: "Column names: [id, title, type, importance]"
|
|
2834
|
+
},
|
|
2835
|
+
rows: {
|
|
2836
|
+
type: "array",
|
|
2837
|
+
items: { type: "array" },
|
|
2838
|
+
description: "Each row: [id, title, type, importance]. Fetch full content via memory-detail"
|
|
2839
|
+
}
|
|
2840
|
+
},
|
|
2841
|
+
required: ["columns", "rows"]
|
|
2842
|
+
}
|
|
2843
|
+
},
|
|
2844
|
+
required: ["schema", "query", "count", "total", "offset", "limit", "results"]
|
|
2845
|
+
}
|
|
2846
|
+
},
|
|
2847
|
+
{
|
|
2848
|
+
name: "memory-summarize",
|
|
2849
|
+
title: "Memory Summarize",
|
|
2850
|
+
description: "Update the summary for a repository",
|
|
2851
|
+
annotations: {
|
|
2852
|
+
readOnlyHint: false,
|
|
2853
|
+
idempotentHint: false,
|
|
2854
|
+
openWorldHint: false
|
|
2855
|
+
},
|
|
2856
|
+
inputSchema: {
|
|
2857
|
+
type: "object",
|
|
2858
|
+
properties: {
|
|
2859
|
+
repo: { type: "string", description: "Repository name" },
|
|
2860
|
+
signals: {
|
|
2861
|
+
type: "array",
|
|
2862
|
+
items: { type: "string", maxLength: 200 },
|
|
2863
|
+
minItems: 1,
|
|
2864
|
+
description: "High-level signals to include in summary"
|
|
2865
|
+
},
|
|
2866
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the summary." }
|
|
2867
|
+
},
|
|
2868
|
+
required: ["repo", "signals"]
|
|
2869
|
+
},
|
|
2870
|
+
outputSchema: {
|
|
2871
|
+
type: "object",
|
|
2872
|
+
properties: {
|
|
2873
|
+
success: { type: "boolean" },
|
|
2874
|
+
repo: { type: "string" },
|
|
2875
|
+
summary: { type: "string" },
|
|
2876
|
+
signalCount: { type: "number" }
|
|
2877
|
+
},
|
|
2878
|
+
required: ["success", "repo", "summary", "signalCount"]
|
|
2879
|
+
}
|
|
2880
|
+
},
|
|
2881
|
+
{
|
|
2882
|
+
name: "memory-delete",
|
|
2883
|
+
title: "Memory Delete",
|
|
2884
|
+
description: "Soft-delete one or more memory entries. Supports single 'id' or bulk 'ids'.",
|
|
2885
|
+
annotations: {
|
|
2886
|
+
readOnlyHint: false,
|
|
2887
|
+
idempotentHint: false,
|
|
2888
|
+
destructiveHint: true,
|
|
2889
|
+
openWorldHint: false
|
|
2890
|
+
},
|
|
2891
|
+
inputSchema: {
|
|
2892
|
+
type: "object",
|
|
2893
|
+
properties: {
|
|
2894
|
+
repo: { type: "string", description: "Repository name (optional for single id)" },
|
|
2895
|
+
id: { type: "string", format: "uuid", description: "Memory entry ID to delete" },
|
|
2896
|
+
ids: {
|
|
2897
|
+
type: "array",
|
|
2898
|
+
items: { type: "string", format: "uuid" },
|
|
2899
|
+
minItems: 1,
|
|
2900
|
+
description: "Array of memory IDs to delete"
|
|
2901
|
+
},
|
|
2902
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
2903
|
+
}
|
|
2904
|
+
},
|
|
2905
|
+
outputSchema: {
|
|
2906
|
+
type: "object",
|
|
2907
|
+
properties: {
|
|
2908
|
+
success: { type: "boolean" },
|
|
2909
|
+
id: { type: "string" },
|
|
2910
|
+
ids: { type: "array", items: { type: "string" } },
|
|
2911
|
+
repo: { type: "string" },
|
|
2912
|
+
deletedCount: { type: "number" }
|
|
2913
|
+
},
|
|
2914
|
+
required: ["success"]
|
|
2915
|
+
}
|
|
2916
|
+
},
|
|
2917
|
+
{
|
|
2918
|
+
name: "memory-recap",
|
|
2919
|
+
title: "Memory Recap",
|
|
2920
|
+
description: "AGGREGATED OVERVIEW LAYER: Returns stats (counts by type) and a pointer table of top memories [id, title, type, importance]. NO content. Use for orientation only \u2014 retrieve full memory via memory-detail.",
|
|
3001
2921
|
annotations: {
|
|
3002
2922
|
readOnlyHint: true,
|
|
3003
2923
|
idempotentHint: true,
|
|
3004
2924
|
openWorldHint: false
|
|
3005
2925
|
},
|
|
3006
|
-
execution: {
|
|
3007
|
-
taskSupport: "optional"
|
|
3008
|
-
},
|
|
3009
2926
|
inputSchema: {
|
|
3010
2927
|
type: "object",
|
|
3011
2928
|
properties: {
|
|
3012
|
-
repo: { type: "string", description: "Repository name
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
2929
|
+
repo: { type: "string", description: "Repository name (required)" },
|
|
2930
|
+
limit: {
|
|
2931
|
+
type: "number",
|
|
2932
|
+
minimum: 1,
|
|
2933
|
+
maximum: 50,
|
|
2934
|
+
default: 20,
|
|
2935
|
+
description: "Maximum number of top memories to return in the pointer table"
|
|
3017
2936
|
},
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
description: "Allow the sampled model to call local memory/task tools during synthesis when the client supports sampling.tools."
|
|
2937
|
+
offset: {
|
|
2938
|
+
type: "number",
|
|
2939
|
+
minimum: 0,
|
|
2940
|
+
default: 0,
|
|
2941
|
+
description: "Number of memories to skip for pagination (optional, default 0)"
|
|
3024
2942
|
},
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
2943
|
+
structured: {
|
|
2944
|
+
type: "boolean",
|
|
2945
|
+
default: false,
|
|
2946
|
+
description: "If true, returns structured JSON without the text content summary."
|
|
2947
|
+
}
|
|
3028
2948
|
},
|
|
3029
|
-
required: ["
|
|
2949
|
+
required: ["repo"]
|
|
3030
2950
|
},
|
|
3031
2951
|
outputSchema: {
|
|
3032
2952
|
type: "object",
|
|
3033
2953
|
properties: {
|
|
2954
|
+
schema: { type: "string", enum: ["memory-recap"] },
|
|
3034
2955
|
repo: { type: "string" },
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
2956
|
+
count: { type: "number", description: "Number of rows in the top pointer table" },
|
|
2957
|
+
total: { type: "number", description: "Total active memories in repo" },
|
|
2958
|
+
offset: { type: "number" },
|
|
2959
|
+
limit: { type: "number" },
|
|
2960
|
+
stats: {
|
|
2961
|
+
type: "object",
|
|
2962
|
+
properties: {
|
|
2963
|
+
by_type: {
|
|
2964
|
+
type: "object",
|
|
2965
|
+
description: "Count of active memories per type (e.g. { decision: 3, code_fact: 7 })"
|
|
2966
|
+
}
|
|
2967
|
+
},
|
|
2968
|
+
required: ["by_type"]
|
|
2969
|
+
},
|
|
2970
|
+
top: {
|
|
2971
|
+
type: "object",
|
|
2972
|
+
properties: {
|
|
2973
|
+
columns: {
|
|
2974
|
+
type: "array",
|
|
2975
|
+
items: { type: "string" },
|
|
2976
|
+
description: "Column names: [id, title, type, importance]"
|
|
2977
|
+
},
|
|
2978
|
+
rows: {
|
|
2979
|
+
type: "array",
|
|
2980
|
+
items: { type: "array" },
|
|
2981
|
+
description: "Each row: [id, title, type, importance]. Fetch full content via memory-detail"
|
|
2982
|
+
}
|
|
2983
|
+
},
|
|
2984
|
+
required: ["columns", "rows"]
|
|
2985
|
+
}
|
|
3041
2986
|
},
|
|
3042
|
-
required: ["repo", "
|
|
2987
|
+
required: ["schema", "repo", "count", "total", "offset", "limit", "stats", "top"]
|
|
3043
2988
|
}
|
|
3044
2989
|
},
|
|
3045
2990
|
{
|
|
3046
|
-
name: "task-create
|
|
3047
|
-
title: "
|
|
3048
|
-
description: "
|
|
2991
|
+
name: "task-create",
|
|
2992
|
+
title: "Task Create",
|
|
2993
|
+
description: "Register one or more new tasks in a repository. task_code must be unique within the repository. Supports single task object or an array of tasks for bulk creation.",
|
|
3049
2994
|
annotations: {
|
|
3050
2995
|
readOnlyHint: false,
|
|
3051
2996
|
idempotentHint: false,
|
|
3052
|
-
destructiveHint: false,
|
|
3053
2997
|
openWorldHint: false
|
|
3054
2998
|
},
|
|
3055
2999
|
inputSchema: {
|
|
3056
3000
|
type: "object",
|
|
3057
3001
|
properties: {
|
|
3058
|
-
repo: {
|
|
3002
|
+
repo: { type: "string", description: "Repository name" },
|
|
3003
|
+
task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
|
|
3004
|
+
phase: { type: "string", description: "Project phase (Required for single task)" },
|
|
3005
|
+
title: {
|
|
3059
3006
|
type: "string",
|
|
3060
|
-
|
|
3007
|
+
minLength: 3,
|
|
3008
|
+
maxLength: 100,
|
|
3009
|
+
description: "Task objective (Required for single task)"
|
|
3010
|
+
},
|
|
3011
|
+
description: { type: "string", description: "Detailed description (Required for single task)" },
|
|
3012
|
+
status: {
|
|
3013
|
+
type: "string",
|
|
3014
|
+
enum: ["backlog", "pending"],
|
|
3015
|
+
default: "backlog",
|
|
3016
|
+
description: "New tasks MUST start in 'backlog' if there are already 10 pending tasks. Otherwise can start in 'pending'."
|
|
3061
3017
|
},
|
|
3062
|
-
task_code: { type: "string" },
|
|
3063
|
-
phase: { type: "string" },
|
|
3064
|
-
title: { type: "string", minLength: 3, maxLength: 100 },
|
|
3065
|
-
description: { type: "string", minLength: 1 },
|
|
3066
|
-
status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
|
|
3067
3018
|
priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
|
|
3068
3019
|
agent: { type: "string" },
|
|
3069
3020
|
role: { type: "string" },
|
|
3070
3021
|
doc_path: { type: "string" },
|
|
3022
|
+
tags: { type: "array", items: { type: "string" } },
|
|
3023
|
+
metadata: { type: "object" },
|
|
3024
|
+
parent_id: { type: "string", format: "uuid" },
|
|
3025
|
+
depends_on: { type: "string", format: "uuid" },
|
|
3026
|
+
est_tokens: { type: "number", minimum: 0, description: "Estimated tokens budget for this task" },
|
|
3027
|
+
tasks: {
|
|
3028
|
+
type: "array",
|
|
3029
|
+
items: {
|
|
3030
|
+
type: "object",
|
|
3031
|
+
properties: {
|
|
3032
|
+
task_code: { type: "string" },
|
|
3033
|
+
phase: { type: "string" },
|
|
3034
|
+
title: { type: "string", minLength: 3, maxLength: 100 },
|
|
3035
|
+
description: { type: "string" },
|
|
3036
|
+
status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
|
|
3037
|
+
priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
|
|
3038
|
+
agent: { type: "string" },
|
|
3039
|
+
role: { type: "string" },
|
|
3040
|
+
doc_path: { type: "string" },
|
|
3041
|
+
tags: { type: "array", items: { type: "string" } },
|
|
3042
|
+
metadata: { type: "object" },
|
|
3043
|
+
parent_id: { type: "string", format: "uuid" },
|
|
3044
|
+
depends_on: { type: "string", format: "uuid" },
|
|
3045
|
+
est_tokens: { type: "number", minimum: 0 }
|
|
3046
|
+
},
|
|
3047
|
+
required: ["task_code", "phase", "title", "description"]
|
|
3048
|
+
},
|
|
3049
|
+
description: "Array of tasks for bulk creation"
|
|
3050
|
+
},
|
|
3071
3051
|
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
3072
|
-
}
|
|
3052
|
+
},
|
|
3053
|
+
required: ["repo"]
|
|
3073
3054
|
},
|
|
3074
3055
|
outputSchema: {
|
|
3075
3056
|
type: "object",
|
|
3076
3057
|
properties: {
|
|
3077
|
-
|
|
3058
|
+
success: { type: "boolean" },
|
|
3059
|
+
id: { type: "string" },
|
|
3078
3060
|
task_code: { type: "string" },
|
|
3061
|
+
repo: { type: "string" },
|
|
3079
3062
|
phase: { type: "string" },
|
|
3080
3063
|
title: { type: "string" },
|
|
3081
3064
|
status: { type: "string" },
|
|
3082
|
-
priority: { type: "number" }
|
|
3065
|
+
priority: { type: "number" },
|
|
3066
|
+
createdCount: { type: "number" },
|
|
3067
|
+
taskCodes: { type: "array", items: { type: "string" } }
|
|
3083
3068
|
},
|
|
3084
|
-
required: ["
|
|
3069
|
+
required: ["success", "repo"]
|
|
3085
3070
|
}
|
|
3086
3071
|
},
|
|
3087
3072
|
{
|
|
3088
|
-
name: "
|
|
3089
|
-
title: "
|
|
3090
|
-
description: "
|
|
3073
|
+
name: "task-update",
|
|
3074
|
+
title: "Task Update",
|
|
3075
|
+
description: "Update one or more tasks. Supports single update via 'id' or bulk update via 'ids'. Provide only the fields that need to be changed. MANDATORY WORKFLOW: You cannot move a task from 'pending' or 'blocked' directly to 'completed'. You MUST move it to 'in_progress' first. When changing status to 'completed', include 'est_tokens' with the estimated total tokens actually used for the task.",
|
|
3076
|
+
annotations: {
|
|
3077
|
+
readOnlyHint: false,
|
|
3078
|
+
idempotentHint: false,
|
|
3079
|
+
openWorldHint: false
|
|
3080
|
+
},
|
|
3091
3081
|
inputSchema: {
|
|
3092
3082
|
type: "object",
|
|
3093
3083
|
properties: {
|
|
3094
|
-
|
|
3095
|
-
|
|
3084
|
+
repo: { type: "string", description: "Repository name" },
|
|
3085
|
+
id: { type: "string", format: "uuid", description: "Task ID (for single update)" },
|
|
3086
|
+
ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk update)" },
|
|
3087
|
+
task_code: { type: "string" },
|
|
3088
|
+
phase: { type: "string" },
|
|
3089
|
+
title: { type: "string", minLength: 3, maxLength: 100 },
|
|
3090
|
+
description: { type: "string" },
|
|
3091
|
+
status: {
|
|
3092
|
+
type: "string",
|
|
3093
|
+
enum: ["backlog", "pending", "in_progress", "completed", "canceled", "blocked"],
|
|
3094
|
+
description: "New status. Transitions from 'backlog', 'pending' or 'blocked' to 'completed' are NOT allowed."
|
|
3095
|
+
},
|
|
3096
|
+
priority: { type: "number", minimum: 1, maximum: 5 },
|
|
3097
|
+
agent: { type: "string" },
|
|
3098
|
+
role: { type: "string" },
|
|
3099
|
+
model: { type: "string" },
|
|
3100
|
+
comment: {
|
|
3101
|
+
type: "string",
|
|
3102
|
+
description: "REQUIRED when changing task status. Explain WHY the status is changing (e.g., 'Starting implementation', 'Blocked by missing API docs', 'Verified fix')."
|
|
3103
|
+
},
|
|
3104
|
+
doc_path: { type: "string" },
|
|
3105
|
+
tags: { type: "array", items: { type: "string" } },
|
|
3106
|
+
metadata: { type: "object" },
|
|
3107
|
+
parent_id: { type: "string", format: "uuid" },
|
|
3108
|
+
depends_on: { type: "string", format: "uuid" },
|
|
3109
|
+
est_tokens: {
|
|
3110
|
+
type: "number",
|
|
3111
|
+
minimum: 0,
|
|
3112
|
+
description: "Estimated total tokens actually used for this task. Required when status changes to 'completed'."
|
|
3113
|
+
},
|
|
3114
|
+
force: {
|
|
3115
|
+
type: "boolean",
|
|
3116
|
+
description: "If true, bypasses status transition validation (e.g. pending -> completed)."
|
|
3117
|
+
},
|
|
3118
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
3096
3119
|
},
|
|
3097
|
-
required: ["
|
|
3120
|
+
required: ["repo"]
|
|
3121
|
+
},
|
|
3122
|
+
outputSchema: {
|
|
3123
|
+
type: "object",
|
|
3124
|
+
properties: {
|
|
3125
|
+
success: { type: "boolean" },
|
|
3126
|
+
id: { type: "string" },
|
|
3127
|
+
ids: { type: "array", items: { type: "string" } },
|
|
3128
|
+
repo: { type: "string" },
|
|
3129
|
+
status: { type: "string" },
|
|
3130
|
+
archivedToMemory: { type: "boolean" },
|
|
3131
|
+
updatedFields: {
|
|
3132
|
+
type: "array",
|
|
3133
|
+
items: { type: "string" }
|
|
3134
|
+
},
|
|
3135
|
+
updatedCount: { type: "number" }
|
|
3136
|
+
},
|
|
3137
|
+
required: ["success", "repo"]
|
|
3098
3138
|
}
|
|
3099
3139
|
},
|
|
3100
3140
|
{
|
|
3101
|
-
name: "task-
|
|
3102
|
-
title: "Task
|
|
3103
|
-
description: "
|
|
3141
|
+
name: "task-delete",
|
|
3142
|
+
title: "Task Delete",
|
|
3143
|
+
description: "Delete one or more tasks from a repository. Supports single 'id' or bulk 'ids'.",
|
|
3144
|
+
annotations: {
|
|
3145
|
+
readOnlyHint: false,
|
|
3146
|
+
idempotentHint: false,
|
|
3147
|
+
destructiveHint: true,
|
|
3148
|
+
openWorldHint: false
|
|
3149
|
+
},
|
|
3104
3150
|
inputSchema: {
|
|
3105
3151
|
type: "object",
|
|
3106
3152
|
properties: {
|
|
3107
|
-
repo: { type: "string", description: "Repository name" },
|
|
3108
|
-
id: { type: "string", format: "uuid", description: "Task ID (
|
|
3109
|
-
|
|
3110
|
-
structured: {
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3153
|
+
repo: { type: "string", description: "Repository name" },
|
|
3154
|
+
id: { type: "string", format: "uuid", description: "Task ID (for single deletion)" },
|
|
3155
|
+
ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk deletion)" },
|
|
3156
|
+
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
3157
|
+
},
|
|
3158
|
+
required: ["repo"]
|
|
3159
|
+
},
|
|
3160
|
+
outputSchema: {
|
|
3161
|
+
type: "object",
|
|
3162
|
+
properties: {
|
|
3163
|
+
success: { type: "boolean" },
|
|
3164
|
+
id: { type: "string" },
|
|
3165
|
+
ids: { type: "array", items: { type: "string" } },
|
|
3166
|
+
repo: { type: "string" },
|
|
3167
|
+
deletedCount: { type: "number" }
|
|
3115
3168
|
},
|
|
3116
|
-
required: ["repo"]
|
|
3169
|
+
required: ["success", "repo"]
|
|
3117
3170
|
}
|
|
3118
3171
|
},
|
|
3119
3172
|
{
|
|
3120
|
-
name: "
|
|
3121
|
-
title: "
|
|
3122
|
-
description: "
|
|
3173
|
+
name: "task-list",
|
|
3174
|
+
title: "Task List",
|
|
3175
|
+
description: "PRIMARY navigation and search tool for tasks. Returns a compact tabular list of tasks (id, task_code, title, status, priority). Defaults to in_progress and pending tasks. Use 'query' to filter by code, title, or description. Use 'status' (comma-separated) for specific filters. AGENTS: call this once at start, pick ONE task, then call task-detail.",
|
|
3123
3176
|
annotations: {
|
|
3124
|
-
readOnlyHint:
|
|
3125
|
-
idempotentHint:
|
|
3126
|
-
destructiveHint: false,
|
|
3177
|
+
readOnlyHint: true,
|
|
3178
|
+
idempotentHint: true,
|
|
3127
3179
|
openWorldHint: false
|
|
3128
3180
|
},
|
|
3129
3181
|
inputSchema: {
|
|
3130
3182
|
type: "object",
|
|
3131
3183
|
properties: {
|
|
3132
|
-
|
|
3184
|
+
repo: {
|
|
3133
3185
|
type: "string",
|
|
3134
|
-
|
|
3135
|
-
"code_fact",
|
|
3136
|
-
"decision",
|
|
3137
|
-
"mistake",
|
|
3138
|
-
"pattern",
|
|
3139
|
-
"agent_handoff",
|
|
3140
|
-
"agent_registered",
|
|
3141
|
-
"file_claim",
|
|
3142
|
-
"task_archive"
|
|
3143
|
-
],
|
|
3144
|
-
description: "Type of memory being stored"
|
|
3186
|
+
description: "Repository name"
|
|
3145
3187
|
},
|
|
3146
|
-
|
|
3188
|
+
status: {
|
|
3147
3189
|
type: "string",
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
description: "Short human-readable title for the memory. Do not embed bracketed metadata like agent/role/date prefixes here."
|
|
3190
|
+
default: "in_progress,pending",
|
|
3191
|
+
description: "Comma-separated status filter (backlog, pending, in_progress, completed, canceled, blocked). Defaults to 'in_progress,pending'."
|
|
3151
3192
|
},
|
|
3152
|
-
|
|
3193
|
+
phase: {
|
|
3153
3194
|
type: "string",
|
|
3154
|
-
|
|
3155
|
-
description: "The memory content"
|
|
3156
|
-
},
|
|
3157
|
-
importance: {
|
|
3158
|
-
type: "number",
|
|
3159
|
-
minimum: 1,
|
|
3160
|
-
maximum: 5,
|
|
3161
|
-
description: "Importance score (1-5)"
|
|
3195
|
+
description: "Filter by phase (e.g., 'research', 'implementation')"
|
|
3162
3196
|
},
|
|
3163
|
-
|
|
3197
|
+
query: {
|
|
3164
3198
|
type: "string",
|
|
3165
|
-
description: "
|
|
3199
|
+
description: "Search keyword matching task code, title, or description"
|
|
3166
3200
|
},
|
|
3167
|
-
|
|
3168
|
-
type: "
|
|
3169
|
-
|
|
3201
|
+
limit: {
|
|
3202
|
+
type: "number",
|
|
3203
|
+
minimum: 1,
|
|
3204
|
+
maximum: 100,
|
|
3205
|
+
default: 5,
|
|
3206
|
+
description: "Maximum rows to return (default 5)"
|
|
3170
3207
|
},
|
|
3171
|
-
|
|
3172
|
-
type: "
|
|
3173
|
-
|
|
3208
|
+
offset: {
|
|
3209
|
+
type: "number",
|
|
3210
|
+
minimum: 0,
|
|
3211
|
+
default: 0,
|
|
3212
|
+
description: "Offset for pagination"
|
|
3174
3213
|
},
|
|
3175
|
-
|
|
3214
|
+
structured: {
|
|
3215
|
+
type: "boolean",
|
|
3216
|
+
default: false,
|
|
3217
|
+
description: "If true, returns structured JSON without the text content summary."
|
|
3218
|
+
}
|
|
3219
|
+
},
|
|
3220
|
+
required: ["repo"]
|
|
3221
|
+
},
|
|
3222
|
+
outputSchema: {
|
|
3223
|
+
type: "object",
|
|
3224
|
+
properties: {
|
|
3225
|
+
schema: { type: "string", enum: ["task-list"] },
|
|
3226
|
+
tasks: {
|
|
3176
3227
|
type: "object",
|
|
3177
3228
|
properties: {
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3229
|
+
columns: {
|
|
3230
|
+
type: "array",
|
|
3231
|
+
items: { type: "string" },
|
|
3232
|
+
description: "Column names in order: id, task_code, title, status, priority, comments_count"
|
|
3233
|
+
},
|
|
3234
|
+
rows: {
|
|
3235
|
+
type: "array",
|
|
3236
|
+
items: { type: "array" },
|
|
3237
|
+
description: "Each row: [id, task_code, title, status, priority, comments_count]. Use task-detail to fetch full task."
|
|
3238
|
+
}
|
|
3182
3239
|
},
|
|
3183
|
-
required: ["
|
|
3184
|
-
},
|
|
3185
|
-
tags: {
|
|
3186
|
-
type: "array",
|
|
3187
|
-
items: { type: "string" },
|
|
3188
|
-
description: "Technology stack tags (e.g., ['filament', 'laravel'])"
|
|
3189
|
-
},
|
|
3190
|
-
metadata: {
|
|
3191
|
-
type: "object",
|
|
3192
|
-
description: "Structured metadata for non-title context such as source agent, claim fields, or timestamps"
|
|
3193
|
-
},
|
|
3194
|
-
is_global: {
|
|
3195
|
-
type: "boolean",
|
|
3196
|
-
description: "If true, this memory is shared across all repositories"
|
|
3240
|
+
required: ["columns", "rows"]
|
|
3197
3241
|
},
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the stored memory." }
|
|
3242
|
+
count: { type: "number" },
|
|
3243
|
+
offset: { type: "number" }
|
|
3201
3244
|
},
|
|
3202
|
-
required: ["
|
|
3245
|
+
required: ["schema", "tasks", "count"]
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
];
|
|
3249
|
+
|
|
3250
|
+
// src/mcp/utils/pagination.ts
|
|
3251
|
+
function encodeCursor(offset) {
|
|
3252
|
+
return Buffer.from(String(offset), "utf8").toString("base64");
|
|
3253
|
+
}
|
|
3254
|
+
function decodeCursor(cursor) {
|
|
3255
|
+
if (cursor === void 0 || cursor === null || cursor === "") {
|
|
3256
|
+
return 0;
|
|
3257
|
+
}
|
|
3258
|
+
if (typeof cursor !== "string" || cursor.trim() === "") {
|
|
3259
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
3260
|
+
}
|
|
3261
|
+
let decoded;
|
|
3262
|
+
try {
|
|
3263
|
+
decoded = Buffer.from(cursor, "base64").toString("utf8");
|
|
3264
|
+
} catch {
|
|
3265
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
3266
|
+
}
|
|
3267
|
+
if (!/^\d+$/.test(decoded)) {
|
|
3268
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
3269
|
+
}
|
|
3270
|
+
const offset = Number.parseInt(decoded, 10);
|
|
3271
|
+
if (!Number.isFinite(offset) || offset < 0) {
|
|
3272
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
3273
|
+
}
|
|
3274
|
+
return offset;
|
|
3275
|
+
}
|
|
3276
|
+
function invalidPaginationParams(message) {
|
|
3277
|
+
const error = new Error(message);
|
|
3278
|
+
error.code = -32602;
|
|
3279
|
+
return error;
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
// src/mcp/utils/completion.ts
|
|
3283
|
+
var MAX_COMPLETION_VALUES = 100;
|
|
3284
|
+
function rankCompletionValues(candidates, input) {
|
|
3285
|
+
const unique = [...new Set(candidates.filter(Boolean))];
|
|
3286
|
+
const needle = input.trim().toLowerCase();
|
|
3287
|
+
if (!needle) {
|
|
3288
|
+
return unique.slice(0, MAX_COMPLETION_VALUES);
|
|
3289
|
+
}
|
|
3290
|
+
return unique.map((value) => ({ value, score: scoreCompletionValue(value, needle) })).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.value.localeCompare(b.value)).map((entry) => entry.value);
|
|
3291
|
+
}
|
|
3292
|
+
function scoreCompletionValue(value, needle) {
|
|
3293
|
+
const haystack = value.toLowerCase();
|
|
3294
|
+
if (haystack === needle) return 100;
|
|
3295
|
+
if (haystack.startsWith(needle)) return 75;
|
|
3296
|
+
if (haystack.includes(needle)) return 50;
|
|
3297
|
+
const compactNeedle = needle.replace(/[\s_-]+/g, "");
|
|
3298
|
+
const compactHaystack = haystack.replace(/[\s_-]+/g, "");
|
|
3299
|
+
if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
|
|
3300
|
+
return 0;
|
|
3301
|
+
}
|
|
3302
|
+
|
|
3303
|
+
// src/mcp/resources/index.ts
|
|
3304
|
+
var DEFAULT_PAGE_SIZE = 25;
|
|
3305
|
+
var MAX_PAGE_SIZE = 100;
|
|
3306
|
+
function listResources(session, params) {
|
|
3307
|
+
const resources = [
|
|
3308
|
+
{
|
|
3309
|
+
uri: "repository://index",
|
|
3310
|
+
name: "Repository Index",
|
|
3311
|
+
title: "Repository Index",
|
|
3312
|
+
description: "List of all known repositories with memory/task counts and last activity",
|
|
3313
|
+
mimeType: "application/json",
|
|
3314
|
+
annotations: {
|
|
3315
|
+
audience: ["assistant"],
|
|
3316
|
+
priority: 1,
|
|
3317
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
3318
|
+
}
|
|
3319
|
+
},
|
|
3320
|
+
{
|
|
3321
|
+
uri: "session://roots",
|
|
3322
|
+
name: "Session Roots",
|
|
3323
|
+
title: "Session Roots",
|
|
3324
|
+
description: session?.roots.length ? "Active workspace roots provided by the MCP client" : "No active workspace roots were provided by the MCP client",
|
|
3325
|
+
mimeType: "application/json",
|
|
3326
|
+
size: Buffer.byteLength(JSON.stringify({ roots: session?.roots ?? [] }), "utf8"),
|
|
3327
|
+
annotations: {
|
|
3328
|
+
audience: ["assistant"],
|
|
3329
|
+
priority: 0.95,
|
|
3330
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
];
|
|
3334
|
+
return paginateEntries("resources", resources, params);
|
|
3335
|
+
}
|
|
3336
|
+
function listResourceTemplates(params) {
|
|
3337
|
+
const templates = [
|
|
3338
|
+
// ── Memory ──────────────────────────────────────────────────────────────
|
|
3339
|
+
{
|
|
3340
|
+
uriTemplate: "repository://{name}/memories",
|
|
3341
|
+
name: "Repository Memories",
|
|
3342
|
+
title: "Repository Memories",
|
|
3343
|
+
description: "All active memory entries for a specific repository",
|
|
3344
|
+
mimeType: "application/json",
|
|
3345
|
+
annotations: { audience: ["assistant"], priority: 0.85 }
|
|
3346
|
+
},
|
|
3347
|
+
{
|
|
3348
|
+
uriTemplate: "repository://{name}/memories?search={search}&type={type}&tag={tag}",
|
|
3349
|
+
name: "Filtered Repository Memories",
|
|
3350
|
+
title: "Filtered Repository Memories",
|
|
3351
|
+
description: "Filter or search memories within a repository by keyword, type, or tag",
|
|
3352
|
+
mimeType: "application/json",
|
|
3353
|
+
annotations: { audience: ["assistant"], priority: 0.8 }
|
|
3354
|
+
},
|
|
3355
|
+
{
|
|
3356
|
+
uriTemplate: "memory://{id}",
|
|
3357
|
+
name: "Memory Detail",
|
|
3358
|
+
title: "Memory Detail",
|
|
3359
|
+
description: "Full content and statistics for a specific memory UUID",
|
|
3360
|
+
mimeType: "application/json",
|
|
3361
|
+
annotations: { audience: ["assistant"], priority: 0.75 }
|
|
3362
|
+
},
|
|
3363
|
+
// ── Tasks ────────────────────────────────────────────────────────────────
|
|
3364
|
+
{
|
|
3365
|
+
uriTemplate: "repository://{name}/tasks",
|
|
3366
|
+
name: "Repository Tasks",
|
|
3367
|
+
title: "Repository Tasks",
|
|
3368
|
+
description: "All active tasks for a specific repository",
|
|
3369
|
+
mimeType: "application/json",
|
|
3370
|
+
annotations: { audience: ["assistant"], priority: 0.9 }
|
|
3203
3371
|
},
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
type: { type: "string" },
|
|
3212
|
-
title: { type: "string" },
|
|
3213
|
-
error: { type: "string" },
|
|
3214
|
-
message: { type: "string" }
|
|
3215
|
-
},
|
|
3216
|
-
required: ["success"]
|
|
3217
|
-
}
|
|
3218
|
-
},
|
|
3219
|
-
{
|
|
3220
|
-
name: "memory-acknowledge",
|
|
3221
|
-
title: "Memory Acknowledge",
|
|
3222
|
-
description: "Acknowledge the use of a memory or report its irrelevance/contradiction. Mandatory after using memory to generate code.",
|
|
3223
|
-
annotations: {
|
|
3224
|
-
readOnlyHint: false,
|
|
3225
|
-
idempotentHint: false,
|
|
3226
|
-
openWorldHint: false
|
|
3372
|
+
{
|
|
3373
|
+
uriTemplate: "repository://{name}/tasks?status={status}&priority={priority}",
|
|
3374
|
+
name: "Filtered Repository Tasks",
|
|
3375
|
+
title: "Filtered Repository Tasks",
|
|
3376
|
+
description: "Filter tasks within a repository by status or priority level",
|
|
3377
|
+
mimeType: "application/json",
|
|
3378
|
+
annotations: { audience: ["assistant"], priority: 0.85 }
|
|
3227
3379
|
},
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
},
|
|
3236
|
-
required: ["memory_id", "status"]
|
|
3380
|
+
{
|
|
3381
|
+
uriTemplate: "task://{id}",
|
|
3382
|
+
name: "Task Detail",
|
|
3383
|
+
title: "Task Detail",
|
|
3384
|
+
description: "Full content and comments for a specific task UUID",
|
|
3385
|
+
mimeType: "application/json",
|
|
3386
|
+
annotations: { audience: ["assistant"], priority: 0.8 }
|
|
3237
3387
|
},
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
}
|
|
3247
|
-
},
|
|
3248
|
-
{
|
|
3249
|
-
name: "memory-update",
|
|
3250
|
-
title: "Memory Update",
|
|
3251
|
-
description: "Update an existing memory entry. Keep 'title' concise and move agent/role/date or claim context into 'metadata' instead of the title.",
|
|
3252
|
-
annotations: {
|
|
3253
|
-
readOnlyHint: false,
|
|
3254
|
-
idempotentHint: false,
|
|
3255
|
-
destructiveHint: false,
|
|
3256
|
-
openWorldHint: false
|
|
3388
|
+
// ── Repository extras ────────────────────────────────────────────────────
|
|
3389
|
+
{
|
|
3390
|
+
uriTemplate: "repository://{name}/summary",
|
|
3391
|
+
name: "Repository Summary",
|
|
3392
|
+
title: "Repository Summary",
|
|
3393
|
+
description: "High-level architectural summary for a repository",
|
|
3394
|
+
mimeType: "text/plain",
|
|
3395
|
+
annotations: { audience: ["assistant"], priority: 0.95 }
|
|
3257
3396
|
},
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
"code_fact",
|
|
3266
|
-
"decision",
|
|
3267
|
-
"mistake",
|
|
3268
|
-
"pattern",
|
|
3269
|
-
"agent_handoff",
|
|
3270
|
-
"agent_registered",
|
|
3271
|
-
"file_claim",
|
|
3272
|
-
"task_archive"
|
|
3273
|
-
]
|
|
3274
|
-
},
|
|
3275
|
-
title: { type: "string", minLength: 3, maxLength: 100 },
|
|
3276
|
-
content: { type: "string", minLength: 10 },
|
|
3277
|
-
importance: { type: "number", minimum: 1, maximum: 5 },
|
|
3278
|
-
agent: { type: "string" },
|
|
3279
|
-
role: { type: "string" },
|
|
3280
|
-
status: { type: "string", enum: ["active", "archived"] },
|
|
3281
|
-
supersedes: { type: "string", format: "uuid" },
|
|
3282
|
-
tags: { type: "array", items: { type: "string" } },
|
|
3283
|
-
metadata: { type: "object" },
|
|
3284
|
-
is_global: { type: "boolean" },
|
|
3285
|
-
completed_at: { type: "string" },
|
|
3286
|
-
structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the updated memory." }
|
|
3287
|
-
},
|
|
3288
|
-
required: ["id"]
|
|
3397
|
+
{
|
|
3398
|
+
uriTemplate: "repository://{name}/actions",
|
|
3399
|
+
name: "Repository Actions",
|
|
3400
|
+
title: "Repository Actions",
|
|
3401
|
+
description: "Audit log of agent tool actions scoped to a repository",
|
|
3402
|
+
mimeType: "application/json",
|
|
3403
|
+
annotations: { audience: ["assistant"], priority: 0.6 }
|
|
3289
3404
|
},
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
items: { type: "string" }
|
|
3299
|
-
}
|
|
3300
|
-
},
|
|
3301
|
-
required: ["success", "id", "repo", "updatedFields"]
|
|
3405
|
+
// ── Action detail ────────────────────────────────────────────────────────
|
|
3406
|
+
{
|
|
3407
|
+
uriTemplate: "action://{id}",
|
|
3408
|
+
name: "Action Detail",
|
|
3409
|
+
title: "Action Detail",
|
|
3410
|
+
description: "Full details of a specific audit log entry by integer ID",
|
|
3411
|
+
mimeType: "application/json",
|
|
3412
|
+
annotations: { audience: ["assistant"], priority: 0.55 }
|
|
3302
3413
|
}
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
"file_claim",
|
|
3336
|
-
"task_archive"
|
|
3337
|
-
]
|
|
3414
|
+
];
|
|
3415
|
+
return paginateEntries("resourceTemplates", templates, params);
|
|
3416
|
+
}
|
|
3417
|
+
function completeResourceArgument(resourceUri, argumentName, argumentValue, _contextArguments, dataSources) {
|
|
3418
|
+
if (resourceUri === "repository://{name}/memories" || resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}" || resourceUri === "repository://{name}/tasks" || resourceUri === "repository://{name}/tasks?status={status}&priority={priority}" || resourceUri === "repository://{name}/summary" || resourceUri === "repository://{name}/actions") {
|
|
3419
|
+
if (argumentName === "name") {
|
|
3420
|
+
return rankCompletionValues(dataSources.repos, argumentValue);
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
if (resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}") {
|
|
3424
|
+
if (argumentName === "tag") {
|
|
3425
|
+
return rankCompletionValues(dataSources.tags, argumentValue);
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
throw invalidCompletionParams(`Unknown resource template or argument: ${resourceUri} (${argumentName})`);
|
|
3429
|
+
}
|
|
3430
|
+
function readResource(uri, db, session) {
|
|
3431
|
+
logger.info("[MCP] resource.read", { uri });
|
|
3432
|
+
if (uri === "repository://index") {
|
|
3433
|
+
const repos = db.system.listRepoNavigation();
|
|
3434
|
+
const payload = JSON.stringify(repos, null, 2);
|
|
3435
|
+
return {
|
|
3436
|
+
contents: [
|
|
3437
|
+
{
|
|
3438
|
+
uri,
|
|
3439
|
+
mimeType: "application/json",
|
|
3440
|
+
text: payload,
|
|
3441
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
3442
|
+
annotations: {
|
|
3443
|
+
audience: ["assistant"],
|
|
3444
|
+
priority: 1,
|
|
3445
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
3338
3446
|
}
|
|
3339
|
-
}
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3447
|
+
}
|
|
3448
|
+
]
|
|
3449
|
+
};
|
|
3450
|
+
}
|
|
3451
|
+
if (uri === "session://roots") {
|
|
3452
|
+
const payload = JSON.stringify({ roots: session?.roots ?? [] }, null, 2);
|
|
3453
|
+
return {
|
|
3454
|
+
contents: [
|
|
3455
|
+
{
|
|
3456
|
+
uri,
|
|
3457
|
+
mimeType: "application/json",
|
|
3458
|
+
text: payload,
|
|
3459
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
3460
|
+
annotations: {
|
|
3461
|
+
audience: ["assistant"],
|
|
3462
|
+
priority: 0.95,
|
|
3463
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
]
|
|
3467
|
+
};
|
|
3468
|
+
}
|
|
3469
|
+
const memoryIdMatch = uri.match(/^memory:\/\/([0-9a-f-]{36})$/i);
|
|
3470
|
+
if (memoryIdMatch) {
|
|
3471
|
+
const id = memoryIdMatch[1];
|
|
3472
|
+
const entry = db.memories.getByIdWithStats(id);
|
|
3473
|
+
if (!entry) throw resourceNotFound(`Memory with ID ${id} not found.`, uri);
|
|
3474
|
+
const payload = JSON.stringify(entry, null, 2);
|
|
3475
|
+
return {
|
|
3476
|
+
contents: [
|
|
3477
|
+
{
|
|
3478
|
+
uri,
|
|
3479
|
+
mimeType: "application/json",
|
|
3480
|
+
text: payload,
|
|
3481
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
3482
|
+
annotations: {
|
|
3483
|
+
audience: ["assistant"],
|
|
3484
|
+
priority: 0.75,
|
|
3485
|
+
lastModified: entry.updated_at || entry.created_at
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
]
|
|
3489
|
+
};
|
|
3490
|
+
}
|
|
3491
|
+
const taskIdMatch = uri.match(/^task:\/\/([0-9a-f-]{36})$/i);
|
|
3492
|
+
if (taskIdMatch) {
|
|
3493
|
+
const id = taskIdMatch[1];
|
|
3494
|
+
const task = db.tasks.getTaskById(id);
|
|
3495
|
+
if (!task) throw resourceNotFound(`Task with ID ${id} not found.`, uri);
|
|
3496
|
+
const payload = JSON.stringify(task, null, 2);
|
|
3497
|
+
return {
|
|
3498
|
+
contents: [
|
|
3499
|
+
{
|
|
3500
|
+
uri,
|
|
3501
|
+
mimeType: "application/json",
|
|
3502
|
+
text: payload,
|
|
3503
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
3504
|
+
annotations: {
|
|
3505
|
+
audience: ["assistant"],
|
|
3506
|
+
priority: 0.8,
|
|
3507
|
+
lastModified: task.updated_at || task.created_at
|
|
3353
3508
|
}
|
|
3354
|
-
},
|
|
3355
|
-
structured: {
|
|
3356
|
-
type: "boolean",
|
|
3357
|
-
default: false,
|
|
3358
|
-
description: "If true, returns structured JSON without the text content summary."
|
|
3359
3509
|
}
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
rows: {
|
|
3381
|
-
type: "array",
|
|
3382
|
-
items: { type: "array" },
|
|
3383
|
-
description: "Each row: [id, title, type, importance]. Fetch full content via memory-detail"
|
|
3510
|
+
]
|
|
3511
|
+
};
|
|
3512
|
+
}
|
|
3513
|
+
const repoBase = parseRepoUri(uri);
|
|
3514
|
+
if (repoBase) {
|
|
3515
|
+
const { name, path: repoPath, query } = repoBase;
|
|
3516
|
+
if (repoPath === "summary") {
|
|
3517
|
+
const summary = db.summaries.getSummary(name);
|
|
3518
|
+
const text = summary?.summary || `No summary available for repository: ${name}`;
|
|
3519
|
+
return {
|
|
3520
|
+
contents: [
|
|
3521
|
+
{
|
|
3522
|
+
uri,
|
|
3523
|
+
mimeType: "text/plain",
|
|
3524
|
+
text,
|
|
3525
|
+
size: Buffer.byteLength(text, "utf8"),
|
|
3526
|
+
annotations: {
|
|
3527
|
+
audience: ["assistant"],
|
|
3528
|
+
priority: 0.95,
|
|
3529
|
+
lastModified: summary?.updated_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
3384
3530
|
}
|
|
3385
|
-
}
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
},
|
|
3389
|
-
required: ["schema", "query", "count", "total", "offset", "limit", "results"]
|
|
3531
|
+
}
|
|
3532
|
+
]
|
|
3533
|
+
};
|
|
3390
3534
|
}
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
signalCount: { type: "number" }
|
|
3422
|
-
},
|
|
3423
|
-
required: ["success", "repo", "summary", "signalCount"]
|
|
3535
|
+
if (repoPath === "memories") {
|
|
3536
|
+
const search = query.get("search") || "";
|
|
3537
|
+
const type = query.get("type");
|
|
3538
|
+
const tag = query.get("tag");
|
|
3539
|
+
const result = db.memories.listMemoriesForDashboard({
|
|
3540
|
+
repo: name,
|
|
3541
|
+
type: type || void 0,
|
|
3542
|
+
tag: tag || void 0,
|
|
3543
|
+
search: search || void 0,
|
|
3544
|
+
limit: 50
|
|
3545
|
+
});
|
|
3546
|
+
const entries = result.items;
|
|
3547
|
+
const payload = JSON.stringify(entries, null, 2);
|
|
3548
|
+
return {
|
|
3549
|
+
contents: [
|
|
3550
|
+
{
|
|
3551
|
+
uri,
|
|
3552
|
+
mimeType: "application/json",
|
|
3553
|
+
text: payload,
|
|
3554
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
3555
|
+
annotations: {
|
|
3556
|
+
audience: ["assistant"],
|
|
3557
|
+
priority: 0.85,
|
|
3558
|
+
lastModified: deriveLastModifiedFromCollection(
|
|
3559
|
+
entries.map((e) => e.updated_at || e.created_at)
|
|
3560
|
+
)
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
]
|
|
3564
|
+
};
|
|
3424
3565
|
}
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
openWorldHint: false
|
|
3435
|
-
},
|
|
3436
|
-
inputSchema: {
|
|
3437
|
-
type: "object",
|
|
3438
|
-
properties: {
|
|
3439
|
-
repo: { type: "string", description: "Repository name (optional for single id)" },
|
|
3440
|
-
id: { type: "string", format: "uuid", description: "Memory entry ID to delete" },
|
|
3441
|
-
ids: {
|
|
3442
|
-
type: "array",
|
|
3443
|
-
items: { type: "string", format: "uuid" },
|
|
3444
|
-
minItems: 1,
|
|
3445
|
-
description: "Array of memory IDs to delete"
|
|
3446
|
-
},
|
|
3447
|
-
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
3566
|
+
if (repoPath === "tasks") {
|
|
3567
|
+
const status = query.get("status");
|
|
3568
|
+
const priority = query.get("priority");
|
|
3569
|
+
let tasks;
|
|
3570
|
+
if (status && status !== "all") {
|
|
3571
|
+
const statuses = status.split(",").map((s) => s.trim());
|
|
3572
|
+
tasks = db.tasks.getTasksByMultipleStatuses(name, statuses);
|
|
3573
|
+
} else {
|
|
3574
|
+
tasks = db.tasks.getTasksByMultipleStatuses(name, ["backlog", "pending", "in_progress", "blocked"]);
|
|
3448
3575
|
}
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
success: { type: "boolean" },
|
|
3454
|
-
id: { type: "string" },
|
|
3455
|
-
ids: { type: "array", items: { type: "string" } },
|
|
3456
|
-
repo: { type: "string" },
|
|
3457
|
-
deletedCount: { type: "number" }
|
|
3458
|
-
},
|
|
3459
|
-
required: ["success"]
|
|
3460
|
-
}
|
|
3461
|
-
},
|
|
3462
|
-
{
|
|
3463
|
-
name: "memory-recap",
|
|
3464
|
-
title: "Memory Recap",
|
|
3465
|
-
description: "AGGREGATED OVERVIEW LAYER: Returns stats (counts by type) and a pointer table of top memories [id, title, type, importance]. NO content. Use for orientation only \u2014 retrieve full memory via memory-detail.",
|
|
3466
|
-
annotations: {
|
|
3467
|
-
readOnlyHint: true,
|
|
3468
|
-
idempotentHint: true,
|
|
3469
|
-
openWorldHint: false
|
|
3470
|
-
},
|
|
3471
|
-
inputSchema: {
|
|
3472
|
-
type: "object",
|
|
3473
|
-
properties: {
|
|
3474
|
-
repo: { type: "string", description: "Repository name (required)" },
|
|
3475
|
-
limit: {
|
|
3476
|
-
type: "number",
|
|
3477
|
-
minimum: 1,
|
|
3478
|
-
maximum: 50,
|
|
3479
|
-
default: 20,
|
|
3480
|
-
description: "Maximum number of top memories to return in the pointer table"
|
|
3481
|
-
},
|
|
3482
|
-
offset: {
|
|
3483
|
-
type: "number",
|
|
3484
|
-
minimum: 0,
|
|
3485
|
-
default: 0,
|
|
3486
|
-
description: "Number of memories to skip for pagination (optional, default 0)"
|
|
3487
|
-
},
|
|
3488
|
-
structured: {
|
|
3489
|
-
type: "boolean",
|
|
3490
|
-
default: false,
|
|
3491
|
-
description: "If true, returns structured JSON without the text content summary."
|
|
3576
|
+
if (priority) {
|
|
3577
|
+
const p = Number(priority);
|
|
3578
|
+
if (!isNaN(p)) {
|
|
3579
|
+
tasks = tasks.filter((t) => t.priority === p);
|
|
3492
3580
|
}
|
|
3493
|
-
}
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
type: "object",
|
|
3507
|
-
properties: {
|
|
3508
|
-
by_type: {
|
|
3509
|
-
type: "object",
|
|
3510
|
-
description: "Count of active memories per type (e.g. { decision: 3, code_fact: 7 })"
|
|
3511
|
-
}
|
|
3512
|
-
},
|
|
3513
|
-
required: ["by_type"]
|
|
3514
|
-
},
|
|
3515
|
-
top: {
|
|
3516
|
-
type: "object",
|
|
3517
|
-
properties: {
|
|
3518
|
-
columns: {
|
|
3519
|
-
type: "array",
|
|
3520
|
-
items: { type: "string" },
|
|
3521
|
-
description: "Column names: [id, title, type, importance]"
|
|
3522
|
-
},
|
|
3523
|
-
rows: {
|
|
3524
|
-
type: "array",
|
|
3525
|
-
items: { type: "array" },
|
|
3526
|
-
description: "Each row: [id, title, type, importance]. Fetch full content via memory-detail"
|
|
3581
|
+
}
|
|
3582
|
+
const payload = JSON.stringify(tasks, null, 2);
|
|
3583
|
+
return {
|
|
3584
|
+
contents: [
|
|
3585
|
+
{
|
|
3586
|
+
uri,
|
|
3587
|
+
mimeType: "application/json",
|
|
3588
|
+
text: payload,
|
|
3589
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
3590
|
+
annotations: {
|
|
3591
|
+
audience: ["assistant"],
|
|
3592
|
+
priority: 0.9,
|
|
3593
|
+
lastModified: deriveLastModifiedFromCollection(tasks.map((t) => t.updated_at))
|
|
3527
3594
|
}
|
|
3528
|
-
}
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
},
|
|
3532
|
-
required: ["schema", "repo", "count", "total", "offset", "limit", "stats", "top"]
|
|
3533
|
-
}
|
|
3534
|
-
},
|
|
3535
|
-
{
|
|
3536
|
-
name: "task-create",
|
|
3537
|
-
title: "Task Create",
|
|
3538
|
-
description: "Register one or more new tasks in a repository. task_code must be unique within the repository. Supports single task object or an array of tasks for bulk creation.",
|
|
3539
|
-
annotations: {
|
|
3540
|
-
readOnlyHint: false,
|
|
3541
|
-
idempotentHint: false,
|
|
3542
|
-
openWorldHint: false
|
|
3543
|
-
},
|
|
3544
|
-
inputSchema: {
|
|
3545
|
-
type: "object",
|
|
3546
|
-
properties: {
|
|
3547
|
-
repo: { type: "string", description: "Repository name" },
|
|
3548
|
-
task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
|
|
3549
|
-
phase: { type: "string", description: "Project phase (Required for single task)" },
|
|
3550
|
-
title: {
|
|
3551
|
-
type: "string",
|
|
3552
|
-
minLength: 3,
|
|
3553
|
-
maxLength: 100,
|
|
3554
|
-
description: "Task objective (Required for single task)"
|
|
3555
|
-
},
|
|
3556
|
-
description: { type: "string", description: "Detailed description (Required for single task)" },
|
|
3557
|
-
status: {
|
|
3558
|
-
type: "string",
|
|
3559
|
-
enum: ["backlog", "pending"],
|
|
3560
|
-
default: "backlog",
|
|
3561
|
-
description: "New tasks MUST start in 'backlog' if there are already 10 pending tasks. Otherwise can start in 'pending'."
|
|
3562
|
-
},
|
|
3563
|
-
priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
|
|
3564
|
-
agent: { type: "string" },
|
|
3565
|
-
role: { type: "string" },
|
|
3566
|
-
doc_path: { type: "string" },
|
|
3567
|
-
tags: { type: "array", items: { type: "string" } },
|
|
3568
|
-
metadata: { type: "object" },
|
|
3569
|
-
parent_id: { type: "string", format: "uuid" },
|
|
3570
|
-
depends_on: { type: "string", format: "uuid" },
|
|
3571
|
-
est_tokens: { type: "number", minimum: 0, description: "Estimated tokens budget for this task" },
|
|
3572
|
-
tasks: {
|
|
3573
|
-
type: "array",
|
|
3574
|
-
items: {
|
|
3575
|
-
type: "object",
|
|
3576
|
-
properties: {
|
|
3577
|
-
task_code: { type: "string" },
|
|
3578
|
-
phase: { type: "string" },
|
|
3579
|
-
title: { type: "string", minLength: 3, maxLength: 100 },
|
|
3580
|
-
description: { type: "string" },
|
|
3581
|
-
status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
|
|
3582
|
-
priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
|
|
3583
|
-
agent: { type: "string" },
|
|
3584
|
-
role: { type: "string" },
|
|
3585
|
-
doc_path: { type: "string" },
|
|
3586
|
-
tags: { type: "array", items: { type: "string" } },
|
|
3587
|
-
metadata: { type: "object" },
|
|
3588
|
-
parent_id: { type: "string", format: "uuid" },
|
|
3589
|
-
depends_on: { type: "string", format: "uuid" },
|
|
3590
|
-
est_tokens: { type: "number", minimum: 0 }
|
|
3591
|
-
},
|
|
3592
|
-
required: ["task_code", "phase", "title", "description"]
|
|
3593
|
-
},
|
|
3594
|
-
description: "Array of tasks for bulk creation"
|
|
3595
|
-
},
|
|
3596
|
-
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
3597
|
-
},
|
|
3598
|
-
required: ["repo"]
|
|
3599
|
-
},
|
|
3600
|
-
outputSchema: {
|
|
3601
|
-
type: "object",
|
|
3602
|
-
properties: {
|
|
3603
|
-
success: { type: "boolean" },
|
|
3604
|
-
id: { type: "string" },
|
|
3605
|
-
task_code: { type: "string" },
|
|
3606
|
-
repo: { type: "string" },
|
|
3607
|
-
phase: { type: "string" },
|
|
3608
|
-
title: { type: "string" },
|
|
3609
|
-
status: { type: "string" },
|
|
3610
|
-
priority: { type: "number" },
|
|
3611
|
-
createdCount: { type: "number" },
|
|
3612
|
-
taskCodes: { type: "array", items: { type: "string" } }
|
|
3613
|
-
},
|
|
3614
|
-
required: ["success", "repo"]
|
|
3595
|
+
}
|
|
3596
|
+
]
|
|
3597
|
+
};
|
|
3615
3598
|
}
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
title: { type: "string", minLength: 3, maxLength: 100 },
|
|
3635
|
-
description: { type: "string" },
|
|
3636
|
-
status: {
|
|
3637
|
-
type: "string",
|
|
3638
|
-
enum: ["backlog", "pending", "in_progress", "completed", "canceled", "blocked"],
|
|
3639
|
-
description: "New status. Transitions from 'backlog', 'pending' or 'blocked' to 'completed' are NOT allowed."
|
|
3640
|
-
},
|
|
3641
|
-
priority: { type: "number", minimum: 1, maximum: 5 },
|
|
3642
|
-
agent: { type: "string" },
|
|
3643
|
-
role: { type: "string" },
|
|
3644
|
-
model: { type: "string" },
|
|
3645
|
-
comment: {
|
|
3646
|
-
type: "string",
|
|
3647
|
-
description: "REQUIRED when changing task status. Explain WHY the status is changing (e.g., 'Starting implementation', 'Blocked by missing API docs', 'Verified fix')."
|
|
3648
|
-
},
|
|
3649
|
-
doc_path: { type: "string" },
|
|
3650
|
-
tags: { type: "array", items: { type: "string" } },
|
|
3651
|
-
metadata: { type: "object" },
|
|
3652
|
-
parent_id: { type: "string", format: "uuid" },
|
|
3653
|
-
depends_on: { type: "string", format: "uuid" },
|
|
3654
|
-
est_tokens: {
|
|
3655
|
-
type: "number",
|
|
3656
|
-
minimum: 0,
|
|
3657
|
-
description: "Estimated total tokens actually used for this task. Required when status changes to 'completed'."
|
|
3658
|
-
},
|
|
3659
|
-
force: {
|
|
3660
|
-
type: "boolean",
|
|
3661
|
-
description: "If true, bypasses status transition validation (e.g. pending -> completed)."
|
|
3662
|
-
},
|
|
3663
|
-
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
3664
|
-
},
|
|
3665
|
-
required: ["repo"]
|
|
3666
|
-
},
|
|
3667
|
-
outputSchema: {
|
|
3668
|
-
type: "object",
|
|
3669
|
-
properties: {
|
|
3670
|
-
success: { type: "boolean" },
|
|
3671
|
-
id: { type: "string" },
|
|
3672
|
-
ids: { type: "array", items: { type: "string" } },
|
|
3673
|
-
repo: { type: "string" },
|
|
3674
|
-
status: { type: "string" },
|
|
3675
|
-
archivedToMemory: { type: "boolean" },
|
|
3676
|
-
updatedFields: {
|
|
3677
|
-
type: "array",
|
|
3678
|
-
items: { type: "string" }
|
|
3679
|
-
},
|
|
3680
|
-
updatedCount: { type: "number" }
|
|
3681
|
-
},
|
|
3682
|
-
required: ["success", "repo"]
|
|
3599
|
+
if (repoPath === "actions") {
|
|
3600
|
+
const actions = db.actions.getRecentActions(name, 100);
|
|
3601
|
+
const payload = JSON.stringify(actions, null, 2);
|
|
3602
|
+
return {
|
|
3603
|
+
contents: [
|
|
3604
|
+
{
|
|
3605
|
+
uri,
|
|
3606
|
+
mimeType: "application/json",
|
|
3607
|
+
text: payload,
|
|
3608
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
3609
|
+
annotations: {
|
|
3610
|
+
audience: ["assistant"],
|
|
3611
|
+
priority: 0.6,
|
|
3612
|
+
lastModified: deriveLastModifiedFromCollection(actions.map((a) => a.created_at))
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
]
|
|
3616
|
+
};
|
|
3683
3617
|
}
|
|
3684
|
-
}
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3618
|
+
}
|
|
3619
|
+
const actionIdMatch = uri.match(/^action:\/\/(\d+)$/);
|
|
3620
|
+
if (actionIdMatch) {
|
|
3621
|
+
const id = Number(actionIdMatch[1]);
|
|
3622
|
+
const action = db.actions.getActionById(id);
|
|
3623
|
+
if (!action) throw resourceNotFound(`Action with ID ${id} not found.`, uri);
|
|
3624
|
+
const payload = JSON.stringify(action, null, 2);
|
|
3625
|
+
return {
|
|
3626
|
+
contents: [
|
|
3627
|
+
{
|
|
3628
|
+
uri,
|
|
3629
|
+
mimeType: "application/json",
|
|
3630
|
+
text: payload,
|
|
3631
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
3632
|
+
annotations: {
|
|
3633
|
+
audience: ["assistant"],
|
|
3634
|
+
priority: 0.55,
|
|
3635
|
+
lastModified: action.created_at
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
]
|
|
3639
|
+
};
|
|
3640
|
+
}
|
|
3641
|
+
throw resourceNotFound(`Unknown resource URI: ${uri}`, uri);
|
|
3642
|
+
}
|
|
3643
|
+
function parseRepoUri(uri) {
|
|
3644
|
+
const prefix = "repository://";
|
|
3645
|
+
if (!uri.startsWith(prefix)) return null;
|
|
3646
|
+
const rest = uri.slice(prefix.length);
|
|
3647
|
+
const queryStart = rest.indexOf("?");
|
|
3648
|
+
const withoutQuery = queryStart === -1 ? rest : rest.slice(0, queryStart);
|
|
3649
|
+
const queryString = queryStart === -1 ? "" : rest.slice(queryStart + 1);
|
|
3650
|
+
const slashIdx = withoutQuery.indexOf("/");
|
|
3651
|
+
if (slashIdx === -1) return null;
|
|
3652
|
+
const name = withoutQuery.slice(0, slashIdx);
|
|
3653
|
+
const path6 = withoutQuery.slice(slashIdx + 1);
|
|
3654
|
+
if (!name || !path6) return null;
|
|
3655
|
+
return { name, path: path6, query: new URLSearchParams(queryString) };
|
|
3656
|
+
}
|
|
3657
|
+
function paginateEntries(key, entries, params) {
|
|
3658
|
+
const limit = normalizeLimit(params?.limit);
|
|
3659
|
+
const offset = decodeCursor(params?.cursor);
|
|
3660
|
+
const sliced = entries.slice(offset, offset + limit);
|
|
3661
|
+
const nextOffset = offset + sliced.length;
|
|
3662
|
+
return {
|
|
3663
|
+
[key]: sliced,
|
|
3664
|
+
nextCursor: nextOffset < entries.length ? encodeCursor(nextOffset) : void 0
|
|
3665
|
+
};
|
|
3666
|
+
}
|
|
3667
|
+
function normalizeLimit(limit) {
|
|
3668
|
+
if (typeof limit !== "number" || !Number.isFinite(limit)) {
|
|
3669
|
+
return DEFAULT_PAGE_SIZE;
|
|
3670
|
+
}
|
|
3671
|
+
return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
|
|
3672
|
+
}
|
|
3673
|
+
function deriveLastModifiedFromCollection(values) {
|
|
3674
|
+
const normalized = values.filter((value) => typeof value === "string" && value.length > 0);
|
|
3675
|
+
return normalized.sort().at(-1) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3676
|
+
}
|
|
3677
|
+
function resourceNotFound(message, uri) {
|
|
3678
|
+
const error = new Error(message);
|
|
3679
|
+
error.code = -32002;
|
|
3680
|
+
error.data = { uri };
|
|
3681
|
+
return error;
|
|
3682
|
+
}
|
|
3683
|
+
function invalidCompletionParams(message) {
|
|
3684
|
+
const error = new Error(message);
|
|
3685
|
+
error.code = -32602;
|
|
3686
|
+
return error;
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
// src/mcp/prompts/loader.ts
|
|
3690
|
+
import fs5 from "fs";
|
|
3691
|
+
import path5 from "path";
|
|
3692
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3693
|
+
import matter from "gray-matter";
|
|
3694
|
+
var __filename = fileURLToPath3(import.meta.url);
|
|
3695
|
+
var __dirname2 = path5.dirname(__filename);
|
|
3696
|
+
function findPromptDir() {
|
|
3697
|
+
const candidates = [
|
|
3698
|
+
// Production if chunked into dist/
|
|
3699
|
+
"./prompts",
|
|
3700
|
+
// Production if inlined into dist/mcp/
|
|
3701
|
+
"../prompts",
|
|
3702
|
+
// Dev: /src/mcp/prompts/definitions (next to loader.ts)
|
|
3703
|
+
"./definitions"
|
|
3704
|
+
].map((relPath) => path5.resolve(__dirname2, relPath));
|
|
3705
|
+
for (const dir of candidates) {
|
|
3706
|
+
if (fs5.existsSync(dir)) {
|
|
3707
|
+
const files = fs5.readdirSync(dir);
|
|
3708
|
+
if (files.some((f) => f.endsWith(".md"))) {
|
|
3709
|
+
return dir;
|
|
3710
|
+
}
|
|
3715
3711
|
}
|
|
3716
|
-
}
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
offset: {
|
|
3754
|
-
type: "number",
|
|
3755
|
-
minimum: 0,
|
|
3756
|
-
default: 0,
|
|
3757
|
-
description: "Offset for pagination"
|
|
3758
|
-
},
|
|
3759
|
-
structured: {
|
|
3760
|
-
type: "boolean",
|
|
3761
|
-
default: false,
|
|
3762
|
-
description: "If true, returns structured JSON without the text content summary."
|
|
3712
|
+
}
|
|
3713
|
+
return path5.resolve(__dirname2, "./definitions");
|
|
3714
|
+
}
|
|
3715
|
+
var PROMPT_DIR = findPromptDir();
|
|
3716
|
+
function listPromptFiles() {
|
|
3717
|
+
if (!fs5.existsSync(PROMPT_DIR)) return [];
|
|
3718
|
+
return fs5.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
|
|
3719
|
+
}
|
|
3720
|
+
function loadPromptFromMarkdown(name) {
|
|
3721
|
+
const filePath = path5.join(PROMPT_DIR, `${name}.md`);
|
|
3722
|
+
if (!fs5.existsSync(filePath)) {
|
|
3723
|
+
throw new Error(`Prompt file not found: ${filePath}`);
|
|
3724
|
+
}
|
|
3725
|
+
const fileContent = fs5.readFileSync(filePath, "utf-8");
|
|
3726
|
+
const { data, content } = matter(fileContent);
|
|
3727
|
+
return {
|
|
3728
|
+
name: data.name || name,
|
|
3729
|
+
description: data.description || "",
|
|
3730
|
+
arguments: data.arguments || [],
|
|
3731
|
+
agent: data.agent,
|
|
3732
|
+
content: content.trim()
|
|
3733
|
+
};
|
|
3734
|
+
}
|
|
3735
|
+
|
|
3736
|
+
// src/mcp/prompts/registry.ts
|
|
3737
|
+
function createPromptDefinition(loaded) {
|
|
3738
|
+
return {
|
|
3739
|
+
name: loaded.name,
|
|
3740
|
+
description: loaded.description,
|
|
3741
|
+
arguments: loaded.arguments,
|
|
3742
|
+
agent: loaded.agent,
|
|
3743
|
+
messages: [
|
|
3744
|
+
{
|
|
3745
|
+
role: "user",
|
|
3746
|
+
content: {
|
|
3747
|
+
type: "text",
|
|
3748
|
+
text: loaded.content
|
|
3763
3749
|
}
|
|
3764
|
-
}
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3750
|
+
}
|
|
3751
|
+
]
|
|
3752
|
+
};
|
|
3753
|
+
}
|
|
3754
|
+
var PROMPTS = {};
|
|
3755
|
+
var promptFiles = listPromptFiles();
|
|
3756
|
+
for (const name of promptFiles) {
|
|
3757
|
+
try {
|
|
3758
|
+
PROMPTS[name] = createPromptDefinition(loadPromptFromMarkdown(name));
|
|
3759
|
+
} catch (e) {
|
|
3760
|
+
console.warn(`Failed to load prompt ${name}: ${e}`);
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
async function listPrompts(db, session, params) {
|
|
3764
|
+
const allPrompts = Object.values(PROMPTS).map((p) => ({
|
|
3765
|
+
name: p.name,
|
|
3766
|
+
description: p.description,
|
|
3767
|
+
arguments: p.arguments,
|
|
3768
|
+
metadata: p.agent ? { agent: p.agent } : void 0
|
|
3769
|
+
}));
|
|
3770
|
+
const rawLimit = typeof params?.limit === "number" && Number.isInteger(params?.limit) ? params.limit : 25;
|
|
3771
|
+
const limit = Math.max(1, Math.min(100, Math.trunc(rawLimit)));
|
|
3772
|
+
const offset = decodeCursor(params?.cursor);
|
|
3773
|
+
const sliced = allPrompts.slice(offset, offset + limit);
|
|
3774
|
+
const nextOffset = offset + sliced.length;
|
|
3775
|
+
return {
|
|
3776
|
+
prompts: sliced,
|
|
3777
|
+
nextCursor: nextOffset < allPrompts.length ? encodeCursor(nextOffset) : void 0
|
|
3778
|
+
};
|
|
3779
|
+
}
|
|
3780
|
+
async function getPrompt(name, args = {}, db, session) {
|
|
3781
|
+
const prompt = PROMPTS[name];
|
|
3782
|
+
if (!prompt) {
|
|
3783
|
+
throw new Error(`Prompt not found: ${name}`);
|
|
3784
|
+
}
|
|
3785
|
+
const inferredRepo = inferRepoFromSession(session);
|
|
3786
|
+
const messages = prompt.messages.map((m) => {
|
|
3787
|
+
let text = m.content.text;
|
|
3788
|
+
for (const [key, value] of Object.entries(args)) {
|
|
3789
|
+
text = text.replace(new RegExp(`\\{{${key}\\}}`, "g"), value);
|
|
3791
3790
|
}
|
|
3791
|
+
text = text.replace(/{{current_repo}}/g, inferredRepo || "unknown-repo");
|
|
3792
|
+
return {
|
|
3793
|
+
...m,
|
|
3794
|
+
content: {
|
|
3795
|
+
...m.content,
|
|
3796
|
+
text
|
|
3797
|
+
}
|
|
3798
|
+
};
|
|
3799
|
+
});
|
|
3800
|
+
return {
|
|
3801
|
+
description: prompt.description,
|
|
3802
|
+
messages,
|
|
3803
|
+
metadata: prompt.agent ? { agent: prompt.agent } : void 0
|
|
3804
|
+
};
|
|
3805
|
+
}
|
|
3806
|
+
async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
|
|
3807
|
+
void name;
|
|
3808
|
+
void contextArguments;
|
|
3809
|
+
if (argName === "task_id") {
|
|
3810
|
+
const values = dataSources.tasks.map((t) => t.id);
|
|
3811
|
+
return rankCompletionValues(values, value);
|
|
3792
3812
|
}
|
|
3793
|
-
];
|
|
3813
|
+
return [];
|
|
3814
|
+
}
|
|
3794
3815
|
|
|
3795
3816
|
export {
|
|
3817
|
+
MCP_PROTOCOL_VERSION,
|
|
3818
|
+
CAPABILITIES,
|
|
3796
3819
|
logger,
|
|
3797
3820
|
setLogLevel,
|
|
3798
3821
|
getLogLevel,
|
|
3799
3822
|
addLogSink,
|
|
3800
3823
|
LOG_LEVEL_VALUES,
|
|
3801
|
-
|
|
3802
|
-
decodeCursor,
|
|
3803
|
-
listResources,
|
|
3804
|
-
listResourceTemplates,
|
|
3805
|
-
completeResourceArgument,
|
|
3806
|
-
readResource,
|
|
3807
|
-
createSessionContext,
|
|
3808
|
-
updateSessionFromInitialize,
|
|
3809
|
-
updateSessionRoots,
|
|
3810
|
-
extractRootsFromResult,
|
|
3811
|
-
getFilesystemRoots,
|
|
3812
|
-
isPathWithinRoots,
|
|
3813
|
-
findContainingRoot,
|
|
3814
|
-
inferRepoFromSession,
|
|
3815
|
-
PROMPTS,
|
|
3816
|
-
listPrompts,
|
|
3817
|
-
getPrompt,
|
|
3818
|
-
completePromptArgument,
|
|
3824
|
+
createFileSink,
|
|
3819
3825
|
normalizeRepo,
|
|
3826
|
+
SQLiteStore,
|
|
3820
3827
|
MemoryStoreSchema,
|
|
3821
3828
|
MemoryUpdateSchema,
|
|
3822
3829
|
MemorySearchSchema,
|
|
@@ -3833,7 +3840,22 @@ export {
|
|
|
3833
3840
|
MemoryDetailSchema,
|
|
3834
3841
|
TaskGetSchema,
|
|
3835
3842
|
TOOL_DEFINITIONS,
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3843
|
+
encodeCursor,
|
|
3844
|
+
decodeCursor,
|
|
3845
|
+
listResources,
|
|
3846
|
+
listResourceTemplates,
|
|
3847
|
+
completeResourceArgument,
|
|
3848
|
+
readResource,
|
|
3849
|
+
createSessionContext,
|
|
3850
|
+
updateSessionFromInitialize,
|
|
3851
|
+
updateSessionRoots,
|
|
3852
|
+
extractRootsFromResult,
|
|
3853
|
+
getFilesystemRoots,
|
|
3854
|
+
isPathWithinRoots,
|
|
3855
|
+
findContainingRoot,
|
|
3856
|
+
inferRepoFromSession,
|
|
3857
|
+
PROMPTS,
|
|
3858
|
+
listPrompts,
|
|
3859
|
+
getPrompt,
|
|
3860
|
+
completePromptArgument
|
|
3839
3861
|
};
|