@vheins/local-memory-mcp 0.10.12 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WWVIDZ5P.js → chunk-MGJ7OTPN.js} +864 -864
- package/dist/dashboard/public/assets/index-CbVS6V5d.js +87 -0
- package/dist/dashboard/public/assets/{index-D_W3_eYf.css → index-d4GwN8c5.css} +1 -1
- package/dist/dashboard/public/index.html +2 -2
- package/dist/dashboard/server.js +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/prompts/task-memory-executor.md +117 -1
- package/package.json +1 -1
- package/dist/dashboard/public/assets/index-B1wu-BqO.js +0 -87
|
@@ -1,5 +1,131 @@
|
|
|
1
|
-
// src/mcp/
|
|
1
|
+
// src/mcp/capabilities.ts
|
|
2
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3
|
+
import path2 from "path";
|
|
4
|
+
|
|
5
|
+
// src/mcp/prompts/loader.ts
|
|
2
6
|
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import matter from "gray-matter";
|
|
10
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
var __dirname = path.dirname(__filename);
|
|
12
|
+
function findPromptDir() {
|
|
13
|
+
const candidates = [
|
|
14
|
+
// Production if chunked into dist/
|
|
15
|
+
"./prompts",
|
|
16
|
+
// Production if inlined into dist/mcp/
|
|
17
|
+
"../prompts",
|
|
18
|
+
// Dev: /src/mcp/prompts/definitions (next to loader.ts)
|
|
19
|
+
"./definitions"
|
|
20
|
+
].map((relPath) => path.resolve(__dirname, relPath));
|
|
21
|
+
for (const dir of candidates) {
|
|
22
|
+
if (fs.existsSync(dir)) {
|
|
23
|
+
const files = fs.readdirSync(dir);
|
|
24
|
+
if (files.some((f) => f.endsWith(".md"))) {
|
|
25
|
+
return dir;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return path.resolve(__dirname, "./definitions");
|
|
30
|
+
}
|
|
31
|
+
var PROMPT_DIR = findPromptDir();
|
|
32
|
+
function listPromptFiles() {
|
|
33
|
+
if (!fs.existsSync(PROMPT_DIR)) return [];
|
|
34
|
+
return fs.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
|
|
35
|
+
}
|
|
36
|
+
function loadPromptFromMarkdown(name) {
|
|
37
|
+
const filePath = path.join(PROMPT_DIR, `${name}.md`);
|
|
38
|
+
if (!fs.existsSync(filePath)) {
|
|
39
|
+
throw new Error(`Prompt file not found: ${filePath}`);
|
|
40
|
+
}
|
|
41
|
+
const fileContent = fs.readFileSync(filePath, "utf-8");
|
|
42
|
+
const { data, content } = matter(fileContent);
|
|
43
|
+
return {
|
|
44
|
+
name: data.name || name,
|
|
45
|
+
description: data.description || "",
|
|
46
|
+
arguments: data.arguments || [],
|
|
47
|
+
agent: data.agent,
|
|
48
|
+
content: content.trim()
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function findServerInstructionsDir() {
|
|
52
|
+
const candidates = [
|
|
53
|
+
// Production if chunked into dist/
|
|
54
|
+
"./prompts/server",
|
|
55
|
+
// Production if inlined into dist/mcp/
|
|
56
|
+
"../prompts/server",
|
|
57
|
+
// Dev: /src/mcp/prompts/server (next to loader.ts)
|
|
58
|
+
"./server"
|
|
59
|
+
].map((relPath) => path.resolve(__dirname, relPath));
|
|
60
|
+
for (const dir of candidates) {
|
|
61
|
+
if (fs.existsSync(dir)) {
|
|
62
|
+
const filePath = path.join(dir, "instructions.md");
|
|
63
|
+
if (fs.existsSync(filePath)) {
|
|
64
|
+
return dir;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return path.resolve(__dirname, "./server");
|
|
69
|
+
}
|
|
70
|
+
var SERVER_DIR = findServerInstructionsDir();
|
|
71
|
+
function loadServerInstructions() {
|
|
72
|
+
const filePath = path.join(SERVER_DIR, "instructions.md");
|
|
73
|
+
if (!fs.existsSync(filePath)) {
|
|
74
|
+
throw new Error(`Server instructions file not found: ${filePath}`);
|
|
75
|
+
}
|
|
76
|
+
const fileContent = fs.readFileSync(filePath, "utf-8");
|
|
77
|
+
const { content } = matter(fileContent);
|
|
78
|
+
return content.trim();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/mcp/capabilities.ts
|
|
82
|
+
var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
|
|
83
|
+
var pkgVersion = "0.1.0";
|
|
84
|
+
if ("0.12.0") {
|
|
85
|
+
pkgVersion = "0.12.0";
|
|
86
|
+
} else {
|
|
87
|
+
let searchDir = __dirname2;
|
|
88
|
+
for (let i = 0; i < 5; i++) {
|
|
89
|
+
const candidate = path2.join(searchDir, "package.json");
|
|
90
|
+
try {
|
|
91
|
+
if (fs.existsSync(candidate)) {
|
|
92
|
+
const pkg = JSON.parse(fs.readFileSync(candidate, "utf8"));
|
|
93
|
+
if (pkg.name === "@vheins/local-memory-mcp" && pkg.version) {
|
|
94
|
+
pkgVersion = pkg.version;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
searchDir = path2.dirname(searchDir);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
var MCP_PROTOCOL_VERSION = "2025-03-26";
|
|
104
|
+
var SERVER_INSTRUCTIONS = loadServerInstructions();
|
|
105
|
+
var CAPABILITIES = {
|
|
106
|
+
serverInfo: {
|
|
107
|
+
name: "local-memory-mcp",
|
|
108
|
+
version: pkgVersion,
|
|
109
|
+
instructions: SERVER_INSTRUCTIONS
|
|
110
|
+
},
|
|
111
|
+
capabilities: {
|
|
112
|
+
completions: {},
|
|
113
|
+
logging: {},
|
|
114
|
+
resources: {
|
|
115
|
+
subscribe: true,
|
|
116
|
+
listChanged: true
|
|
117
|
+
},
|
|
118
|
+
tools: {
|
|
119
|
+
listChanged: false
|
|
120
|
+
},
|
|
121
|
+
prompts: {
|
|
122
|
+
listChanged: true
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/mcp/utils/logger.ts
|
|
128
|
+
import fs2 from "fs";
|
|
3
129
|
var LEVELS = {
|
|
4
130
|
debug: 0,
|
|
5
131
|
info: 1,
|
|
@@ -119,11 +245,11 @@ function addLogSink(sink) {
|
|
|
119
245
|
}
|
|
120
246
|
var LOG_LEVEL_VALUES = Object.keys(LEVELS);
|
|
121
247
|
function createFileSink(logDir, maxFiles = 5) {
|
|
122
|
-
|
|
123
|
-
const existing =
|
|
248
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
249
|
+
const existing = fs2.readdirSync(logDir).filter((f) => f.startsWith("mcp-") && f.endsWith(".log")).sort();
|
|
124
250
|
while (existing.length >= maxFiles) {
|
|
125
251
|
try {
|
|
126
|
-
|
|
252
|
+
fs2.unlinkSync(`${logDir}/${existing.shift()}`);
|
|
127
253
|
} catch {
|
|
128
254
|
}
|
|
129
255
|
}
|
|
@@ -133,118 +259,16 @@ function createFileSink(logDir, maxFiles = 5) {
|
|
|
133
259
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${payload.level.toUpperCase()}] [pid:${process.pid}] ${JSON.stringify(payload.data)}
|
|
134
260
|
`;
|
|
135
261
|
try {
|
|
136
|
-
|
|
262
|
+
fs2.appendFileSync(logFile, line);
|
|
137
263
|
} catch {
|
|
138
264
|
}
|
|
139
265
|
};
|
|
140
266
|
}
|
|
141
267
|
|
|
142
|
-
// src/mcp/session.ts
|
|
143
|
-
import path from "path";
|
|
144
|
-
import { fileURLToPath } from "url";
|
|
145
|
-
function createSessionContext() {
|
|
146
|
-
return {
|
|
147
|
-
roots: [],
|
|
148
|
-
supportsRoots: false,
|
|
149
|
-
supportsSampling: false,
|
|
150
|
-
supportsSamplingTools: false,
|
|
151
|
-
supportsElicitation: false,
|
|
152
|
-
supportsElicitationForm: false,
|
|
153
|
-
supportsElicitationUrl: false
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
function updateSessionFromInitialize(session, params) {
|
|
157
|
-
const capabilities = params?.capabilities || {};
|
|
158
|
-
session.clientInfo = params?.clientInfo;
|
|
159
|
-
session.clientCapabilities = capabilities;
|
|
160
|
-
session.supportsRoots = Boolean(capabilities.roots);
|
|
161
|
-
session.supportsSampling = Boolean(capabilities.sampling);
|
|
162
|
-
const sampling = capabilities.sampling;
|
|
163
|
-
session.supportsSamplingTools = Boolean(sampling?.tools);
|
|
164
|
-
session.supportsElicitation = Boolean(capabilities.elicitation);
|
|
165
|
-
session.supportsElicitationForm = supportsElicitationMode(capabilities.elicitation, "form");
|
|
166
|
-
session.supportsElicitationUrl = supportsElicitationMode(capabilities.elicitation, "url");
|
|
167
|
-
}
|
|
168
|
-
function supportsElicitationMode(capability, mode) {
|
|
169
|
-
if (!capability || typeof capability !== "object") {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
const cap = capability;
|
|
173
|
-
if (mode === "form") {
|
|
174
|
-
return Object.keys(cap).length === 0 || typeof cap.form === "object";
|
|
175
|
-
}
|
|
176
|
-
return typeof cap.url === "object";
|
|
177
|
-
}
|
|
178
|
-
function updateSessionRoots(session, roots) {
|
|
179
|
-
const normalized = normalizeRoots(roots);
|
|
180
|
-
const previous = JSON.stringify(session.roots);
|
|
181
|
-
const next = JSON.stringify(normalized);
|
|
182
|
-
session.roots = normalized;
|
|
183
|
-
return previous !== next;
|
|
184
|
-
}
|
|
185
|
-
function normalizeRoots(roots) {
|
|
186
|
-
if (!Array.isArray(roots)) return [];
|
|
187
|
-
const seen = /* @__PURE__ */ new Set();
|
|
188
|
-
const normalized = [];
|
|
189
|
-
for (const root of roots) {
|
|
190
|
-
if (!root || typeof root !== "object") continue;
|
|
191
|
-
const r = root;
|
|
192
|
-
const uri = typeof r.uri === "string" ? r.uri : void 0;
|
|
193
|
-
const name = typeof r.name === "string" ? r.name : void 0;
|
|
194
|
-
if (!uri || seen.has(uri)) continue;
|
|
195
|
-
seen.add(uri);
|
|
196
|
-
normalized.push({ uri, name });
|
|
197
|
-
}
|
|
198
|
-
return normalized;
|
|
199
|
-
}
|
|
200
|
-
function extractRootsFromResult(result) {
|
|
201
|
-
return normalizeRoots(result?.roots);
|
|
202
|
-
}
|
|
203
|
-
function getFilesystemRoots(session) {
|
|
204
|
-
if (!session) return [];
|
|
205
|
-
const resolved = [];
|
|
206
|
-
for (const root of session.roots) {
|
|
207
|
-
if (!root.uri.startsWith("file://")) continue;
|
|
208
|
-
try {
|
|
209
|
-
resolved.push(path.resolve(fileURLToPath(root.uri)));
|
|
210
|
-
} catch {
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return resolved;
|
|
214
|
-
}
|
|
215
|
-
function isPathWithinRoots(targetPath, session) {
|
|
216
|
-
const roots = getFilesystemRoots(session);
|
|
217
|
-
if (roots.length === 0) return true;
|
|
218
|
-
const normalizedTarget = path.resolve(targetPath);
|
|
219
|
-
return roots.some((rootPath) => {
|
|
220
|
-
const relative = path.relative(rootPath, normalizedTarget);
|
|
221
|
-
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
function findContainingRoot(targetPath, session) {
|
|
225
|
-
const roots = getFilesystemRoots(session);
|
|
226
|
-
if (roots.length === 0) return null;
|
|
227
|
-
const normalizedTarget = path.resolve(targetPath);
|
|
228
|
-
for (const rootPath of roots) {
|
|
229
|
-
const relative = path.relative(rootPath, normalizedTarget);
|
|
230
|
-
if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
231
|
-
return rootPath;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
|
-
function inferRepoFromSession(session) {
|
|
237
|
-
const roots = getFilesystemRoots(session);
|
|
238
|
-
if (roots.length === 1) {
|
|
239
|
-
return path.basename(roots[0]);
|
|
240
|
-
}
|
|
241
|
-
return void 0;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
268
|
// src/mcp/storage/sqlite.ts
|
|
245
269
|
import Database from "better-sqlite3";
|
|
246
|
-
import
|
|
247
|
-
import
|
|
270
|
+
import path4 from "path";
|
|
271
|
+
import fs4 from "fs";
|
|
248
272
|
import os from "os";
|
|
249
273
|
|
|
250
274
|
// src/mcp/storage/migrations.ts
|
|
@@ -2736,8 +2760,8 @@ var HandoffEntity = class extends BaseEntity {
|
|
|
2736
2760
|
|
|
2737
2761
|
// src/mcp/storage/write-lock.ts
|
|
2738
2762
|
import lockfile from "proper-lockfile";
|
|
2739
|
-
import
|
|
2740
|
-
import
|
|
2763
|
+
import path3 from "path";
|
|
2764
|
+
import fs3 from "fs";
|
|
2741
2765
|
var LOCK_STALE_MS = 3e4;
|
|
2742
2766
|
var LOCK_RETRY_DELAY_MS = 200;
|
|
2743
2767
|
var LOCK_RETRY_COUNT = 250;
|
|
@@ -2746,9 +2770,9 @@ var WriteLock = class {
|
|
|
2746
2770
|
locked = false;
|
|
2747
2771
|
constructor(dbPath) {
|
|
2748
2772
|
this.lockTarget = dbPath;
|
|
2749
|
-
if (!
|
|
2750
|
-
|
|
2751
|
-
|
|
2773
|
+
if (!fs3.existsSync(dbPath)) {
|
|
2774
|
+
fs3.mkdirSync(path3.dirname(dbPath), { recursive: true });
|
|
2775
|
+
fs3.writeFileSync(dbPath, "");
|
|
2752
2776
|
}
|
|
2753
2777
|
}
|
|
2754
2778
|
/**
|
|
@@ -2800,13 +2824,13 @@ var WriteLock = class {
|
|
|
2800
2824
|
// src/mcp/storage/sqlite.ts
|
|
2801
2825
|
function resolveDbPath() {
|
|
2802
2826
|
if (process.env.MEMORY_DB_PATH) return process.env.MEMORY_DB_PATH;
|
|
2803
|
-
const standardConfigDir = process.platform === "win32" ?
|
|
2804
|
-
const standardPath =
|
|
2805
|
-
if (
|
|
2806
|
-
const legacyPath =
|
|
2807
|
-
if (
|
|
2808
|
-
const localCwdFile =
|
|
2809
|
-
if (
|
|
2827
|
+
const standardConfigDir = process.platform === "win32" ? path4.join(os.homedir(), ".local-memory-mcp") : process.platform === "darwin" ? path4.join(os.homedir(), "Library", "Application Support", "local-memory-mcp") : path4.join(os.homedir(), ".config", "local-memory-mcp");
|
|
2828
|
+
const standardPath = path4.join(standardConfigDir, "memory.db");
|
|
2829
|
+
if (fs4.existsSync(standardPath)) return standardPath;
|
|
2830
|
+
const legacyPath = path4.join(os.homedir(), ".config", "local-memory-mcp", "memory.db");
|
|
2831
|
+
if (fs4.existsSync(legacyPath)) return legacyPath;
|
|
2832
|
+
const localCwdFile = path4.join(process.cwd(), "storage", "memory.db");
|
|
2833
|
+
if (fs4.existsSync(localCwdFile)) return localCwdFile;
|
|
2810
2834
|
return standardPath;
|
|
2811
2835
|
}
|
|
2812
2836
|
var DB_PATH = resolveDbPath();
|
|
@@ -2825,9 +2849,9 @@ var SQLiteStore = class _SQLiteStore {
|
|
|
2825
2849
|
const finalPath = dbPath ?? DB_PATH;
|
|
2826
2850
|
this.dbPathInstance = finalPath;
|
|
2827
2851
|
if (finalPath !== ":memory:") {
|
|
2828
|
-
const dbDir =
|
|
2829
|
-
if (!
|
|
2830
|
-
|
|
2852
|
+
const dbDir = path4.dirname(finalPath);
|
|
2853
|
+
if (!fs4.existsSync(dbDir)) {
|
|
2854
|
+
fs4.mkdirSync(dbDir, { recursive: true });
|
|
2831
2855
|
}
|
|
2832
2856
|
}
|
|
2833
2857
|
this.db = new Database(finalPath);
|
|
@@ -2882,12 +2906,12 @@ var SQLiteStore = class _SQLiteStore {
|
|
|
2882
2906
|
*/
|
|
2883
2907
|
_attemptRecovery(dbPath) {
|
|
2884
2908
|
const backupPath = dbPath + ".backup";
|
|
2885
|
-
if (
|
|
2909
|
+
if (fs4.existsSync(backupPath)) {
|
|
2886
2910
|
logger.warn("[SQLiteStore] Attempting recovery from backup", { backupPath });
|
|
2887
2911
|
try {
|
|
2888
2912
|
const corruptPath = `${dbPath}.corrupt_${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").slice(0, 15)}`;
|
|
2889
|
-
|
|
2890
|
-
|
|
2913
|
+
fs4.copyFileSync(dbPath, corruptPath);
|
|
2914
|
+
fs4.copyFileSync(backupPath, dbPath);
|
|
2891
2915
|
logger.warn("[SQLiteStore] Recovery successful. Corrupt file saved to", { corruptPath });
|
|
2892
2916
|
} catch (err) {
|
|
2893
2917
|
logger.error("[SQLiteStore] Recovery failed", { error: String(err) });
|
|
@@ -2905,7 +2929,7 @@ var SQLiteStore = class _SQLiteStore {
|
|
|
2905
2929
|
try {
|
|
2906
2930
|
this.db.pragma("wal_checkpoint(PASSIVE)");
|
|
2907
2931
|
const backupPath = this.dbPathInstance + ".backup";
|
|
2908
|
-
|
|
2932
|
+
fs4.copyFileSync(this.dbPathInstance, backupPath);
|
|
2909
2933
|
} catch (err) {
|
|
2910
2934
|
logger.warn("[SQLiteStore] Backup failed", { error: String(err) });
|
|
2911
2935
|
}
|
|
@@ -3027,649 +3051,106 @@ var RealVectorStore = class {
|
|
|
3027
3051
|
}
|
|
3028
3052
|
};
|
|
3029
3053
|
|
|
3030
|
-
// src/mcp/
|
|
3031
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3054
|
+
// src/mcp/session.ts
|
|
3032
3055
|
import path5 from "path";
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
import fs4 from "fs";
|
|
3036
|
-
import path4 from "path";
|
|
3037
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3038
|
-
import matter from "gray-matter";
|
|
3039
|
-
var __filename = fileURLToPath2(import.meta.url);
|
|
3040
|
-
var __dirname = path4.dirname(__filename);
|
|
3041
|
-
function findPromptDir() {
|
|
3042
|
-
const candidates = [
|
|
3043
|
-
// Production if chunked into dist/
|
|
3044
|
-
"./prompts",
|
|
3045
|
-
// Production if inlined into dist/mcp/
|
|
3046
|
-
"../prompts",
|
|
3047
|
-
// Dev: /src/mcp/prompts/definitions (next to loader.ts)
|
|
3048
|
-
"./definitions"
|
|
3049
|
-
].map((relPath) => path4.resolve(__dirname, relPath));
|
|
3050
|
-
for (const dir of candidates) {
|
|
3051
|
-
if (fs4.existsSync(dir)) {
|
|
3052
|
-
const files = fs4.readdirSync(dir);
|
|
3053
|
-
if (files.some((f) => f.endsWith(".md"))) {
|
|
3054
|
-
return dir;
|
|
3055
|
-
}
|
|
3056
|
-
}
|
|
3057
|
-
}
|
|
3058
|
-
return path4.resolve(__dirname, "./definitions");
|
|
3059
|
-
}
|
|
3060
|
-
var PROMPT_DIR = findPromptDir();
|
|
3061
|
-
function listPromptFiles() {
|
|
3062
|
-
if (!fs4.existsSync(PROMPT_DIR)) return [];
|
|
3063
|
-
return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
|
|
3064
|
-
}
|
|
3065
|
-
function loadPromptFromMarkdown(name) {
|
|
3066
|
-
const filePath = path4.join(PROMPT_DIR, `${name}.md`);
|
|
3067
|
-
if (!fs4.existsSync(filePath)) {
|
|
3068
|
-
throw new Error(`Prompt file not found: ${filePath}`);
|
|
3069
|
-
}
|
|
3070
|
-
const fileContent = fs4.readFileSync(filePath, "utf-8");
|
|
3071
|
-
const { data, content } = matter(fileContent);
|
|
3056
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3057
|
+
function createSessionContext() {
|
|
3072
3058
|
return {
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3059
|
+
roots: [],
|
|
3060
|
+
supportsRoots: false,
|
|
3061
|
+
supportsSampling: false,
|
|
3062
|
+
supportsSamplingTools: false,
|
|
3063
|
+
supportsElicitation: false,
|
|
3064
|
+
supportsElicitationForm: false,
|
|
3065
|
+
supportsElicitationUrl: false
|
|
3078
3066
|
};
|
|
3079
3067
|
}
|
|
3080
|
-
function
|
|
3081
|
-
const
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3068
|
+
function updateSessionFromInitialize(session, params) {
|
|
3069
|
+
const capabilities = params?.capabilities || {};
|
|
3070
|
+
session.clientInfo = params?.clientInfo;
|
|
3071
|
+
session.clientCapabilities = capabilities;
|
|
3072
|
+
session.supportsRoots = Boolean(capabilities.roots);
|
|
3073
|
+
session.supportsSampling = Boolean(capabilities.sampling);
|
|
3074
|
+
const sampling = capabilities.sampling;
|
|
3075
|
+
session.supportsSamplingTools = Boolean(sampling?.tools);
|
|
3076
|
+
session.supportsElicitation = Boolean(capabilities.elicitation);
|
|
3077
|
+
session.supportsElicitationForm = supportsElicitationMode(capabilities.elicitation, "form");
|
|
3078
|
+
session.supportsElicitationUrl = supportsElicitationMode(capabilities.elicitation, "url");
|
|
3079
|
+
}
|
|
3080
|
+
function supportsElicitationMode(capability, mode) {
|
|
3081
|
+
if (!capability || typeof capability !== "object") {
|
|
3082
|
+
return false;
|
|
3083
|
+
}
|
|
3084
|
+
const cap = capability;
|
|
3085
|
+
if (mode === "form") {
|
|
3086
|
+
return Object.keys(cap).length === 0 || typeof cap.form === "object";
|
|
3096
3087
|
}
|
|
3097
|
-
return
|
|
3088
|
+
return typeof cap.url === "object";
|
|
3098
3089
|
}
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
const
|
|
3102
|
-
|
|
3103
|
-
|
|
3090
|
+
function updateSessionRoots(session, roots) {
|
|
3091
|
+
const normalized = normalizeRoots(roots);
|
|
3092
|
+
const previous = JSON.stringify(session.roots);
|
|
3093
|
+
const next = JSON.stringify(normalized);
|
|
3094
|
+
session.roots = normalized;
|
|
3095
|
+
return previous !== next;
|
|
3096
|
+
}
|
|
3097
|
+
function normalizeRoots(roots) {
|
|
3098
|
+
if (!Array.isArray(roots)) return [];
|
|
3099
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3100
|
+
const normalized = [];
|
|
3101
|
+
for (const root of roots) {
|
|
3102
|
+
if (!root || typeof root !== "object") continue;
|
|
3103
|
+
const r = root;
|
|
3104
|
+
const uri = typeof r.uri === "string" ? r.uri : void 0;
|
|
3105
|
+
const name = typeof r.name === "string" ? r.name : void 0;
|
|
3106
|
+
if (!uri || seen.has(uri)) continue;
|
|
3107
|
+
seen.add(uri);
|
|
3108
|
+
normalized.push({ uri, name });
|
|
3104
3109
|
}
|
|
3105
|
-
|
|
3106
|
-
const { content } = matter(fileContent);
|
|
3107
|
-
return content.trim();
|
|
3110
|
+
return normalized;
|
|
3108
3111
|
}
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
if (
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
for (let i = 0; i < 5; i++) {
|
|
3118
|
-
const candidate = path5.join(searchDir, "package.json");
|
|
3112
|
+
function extractRootsFromResult(result) {
|
|
3113
|
+
return normalizeRoots(result?.roots);
|
|
3114
|
+
}
|
|
3115
|
+
function getFilesystemRoots(session) {
|
|
3116
|
+
if (!session) return [];
|
|
3117
|
+
const resolved = [];
|
|
3118
|
+
for (const root of session.roots) {
|
|
3119
|
+
if (!root.uri.startsWith("file://")) continue;
|
|
3119
3120
|
try {
|
|
3120
|
-
|
|
3121
|
-
const pkg = JSON.parse(fs.readFileSync(candidate, "utf8"));
|
|
3122
|
-
if (pkg.name === "@vheins/local-memory-mcp" && pkg.version) {
|
|
3123
|
-
pkgVersion = pkg.version;
|
|
3124
|
-
break;
|
|
3125
|
-
}
|
|
3126
|
-
}
|
|
3121
|
+
resolved.push(path5.resolve(fileURLToPath3(root.uri)));
|
|
3127
3122
|
} catch {
|
|
3128
3123
|
}
|
|
3129
|
-
searchDir = path5.dirname(searchDir);
|
|
3130
3124
|
}
|
|
3125
|
+
return resolved;
|
|
3131
3126
|
}
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
}
|
|
3140
|
-
capabilities: {
|
|
3141
|
-
completions: {},
|
|
3142
|
-
logging: {},
|
|
3143
|
-
resources: {
|
|
3144
|
-
subscribe: true,
|
|
3145
|
-
listChanged: true
|
|
3146
|
-
},
|
|
3147
|
-
tools: {
|
|
3148
|
-
listChanged: false
|
|
3149
|
-
},
|
|
3150
|
-
prompts: {
|
|
3151
|
-
listChanged: true
|
|
3152
|
-
}
|
|
3153
|
-
}
|
|
3154
|
-
};
|
|
3155
|
-
|
|
3156
|
-
// src/mcp/utils/pagination.ts
|
|
3157
|
-
function encodeCursor(offset) {
|
|
3158
|
-
return Buffer.from(String(offset), "utf8").toString("base64");
|
|
3127
|
+
function isPathWithinRoots(targetPath, session) {
|
|
3128
|
+
const roots = getFilesystemRoots(session);
|
|
3129
|
+
if (roots.length === 0) return true;
|
|
3130
|
+
const normalizedTarget = path5.resolve(targetPath);
|
|
3131
|
+
return roots.some((rootPath) => {
|
|
3132
|
+
const relative = path5.relative(rootPath, normalizedTarget);
|
|
3133
|
+
return relative === "" || !relative.startsWith("..") && !path5.isAbsolute(relative);
|
|
3134
|
+
});
|
|
3159
3135
|
}
|
|
3160
|
-
function
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
decoded = Buffer.from(cursor, "base64").toString("utf8");
|
|
3170
|
-
} catch {
|
|
3171
|
-
throw invalidPaginationParams("Invalid cursor");
|
|
3172
|
-
}
|
|
3173
|
-
if (!/^\d+$/.test(decoded)) {
|
|
3174
|
-
throw invalidPaginationParams("Invalid cursor");
|
|
3175
|
-
}
|
|
3176
|
-
const offset = Number.parseInt(decoded, 10);
|
|
3177
|
-
if (!Number.isFinite(offset) || offset < 0) {
|
|
3178
|
-
throw invalidPaginationParams("Invalid cursor");
|
|
3136
|
+
function findContainingRoot(targetPath, session) {
|
|
3137
|
+
const roots = getFilesystemRoots(session);
|
|
3138
|
+
if (roots.length === 0) return null;
|
|
3139
|
+
const normalizedTarget = path5.resolve(targetPath);
|
|
3140
|
+
for (const rootPath of roots) {
|
|
3141
|
+
const relative = path5.relative(rootPath, normalizedTarget);
|
|
3142
|
+
if (relative === "" || !relative.startsWith("..") && !path5.isAbsolute(relative)) {
|
|
3143
|
+
return rootPath;
|
|
3144
|
+
}
|
|
3179
3145
|
}
|
|
3180
|
-
return
|
|
3181
|
-
}
|
|
3182
|
-
function invalidPaginationParams(message) {
|
|
3183
|
-
const error = new Error(message);
|
|
3184
|
-
error.code = -32602;
|
|
3185
|
-
return error;
|
|
3146
|
+
return null;
|
|
3186
3147
|
}
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
const unique = [...new Set(candidates.filter(Boolean))];
|
|
3192
|
-
const needle = input.trim().toLowerCase();
|
|
3193
|
-
if (!needle) {
|
|
3194
|
-
return unique.slice(0, MAX_COMPLETION_VALUES);
|
|
3148
|
+
function inferRepoFromSession(session) {
|
|
3149
|
+
const roots = getFilesystemRoots(session);
|
|
3150
|
+
if (roots.length === 1) {
|
|
3151
|
+
return path5.basename(roots[0]);
|
|
3195
3152
|
}
|
|
3196
|
-
return
|
|
3197
|
-
}
|
|
3198
|
-
function scoreCompletionValue(value, needle) {
|
|
3199
|
-
const haystack = value.toLowerCase();
|
|
3200
|
-
if (haystack === needle) return 100;
|
|
3201
|
-
if (haystack.startsWith(needle)) return 75;
|
|
3202
|
-
if (haystack.includes(needle)) return 50;
|
|
3203
|
-
const compactNeedle = needle.replace(/[\s_-]+/g, "");
|
|
3204
|
-
const compactHaystack = haystack.replace(/[\s_-]+/g, "");
|
|
3205
|
-
if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
|
|
3206
|
-
return 0;
|
|
3207
|
-
}
|
|
3208
|
-
|
|
3209
|
-
// src/mcp/resources/index.ts
|
|
3210
|
-
var DEFAULT_PAGE_SIZE = 25;
|
|
3211
|
-
var MAX_PAGE_SIZE = 100;
|
|
3212
|
-
function listResources(session, params) {
|
|
3213
|
-
const resources = [
|
|
3214
|
-
{
|
|
3215
|
-
uri: "repository://index",
|
|
3216
|
-
name: "Repository Index",
|
|
3217
|
-
title: "Repository Index",
|
|
3218
|
-
description: "List of all known repositories with memory/task counts and last activity",
|
|
3219
|
-
mimeType: "application/json",
|
|
3220
|
-
annotations: {
|
|
3221
|
-
audience: ["assistant"],
|
|
3222
|
-
priority: 1,
|
|
3223
|
-
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
3224
|
-
}
|
|
3225
|
-
},
|
|
3226
|
-
{
|
|
3227
|
-
uri: "session://roots",
|
|
3228
|
-
name: "Session Roots",
|
|
3229
|
-
title: "Session Roots",
|
|
3230
|
-
description: session?.roots.length ? "Active workspace roots provided by the MCP client" : "No active workspace roots were provided by the MCP client",
|
|
3231
|
-
mimeType: "application/json",
|
|
3232
|
-
size: Buffer.byteLength(JSON.stringify({ roots: session?.roots ?? [] }), "utf8"),
|
|
3233
|
-
annotations: {
|
|
3234
|
-
audience: ["assistant"],
|
|
3235
|
-
priority: 0.95,
|
|
3236
|
-
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
3237
|
-
}
|
|
3238
|
-
}
|
|
3239
|
-
];
|
|
3240
|
-
return paginateEntries("resources", resources, params);
|
|
3241
|
-
}
|
|
3242
|
-
function listResourceTemplates(params) {
|
|
3243
|
-
const templates = [
|
|
3244
|
-
// ── Memory ──────────────────────────────────────────────────────────────
|
|
3245
|
-
{
|
|
3246
|
-
uriTemplate: "repository://{name}/memories",
|
|
3247
|
-
name: "Repository Memories",
|
|
3248
|
-
title: "Repository Memories",
|
|
3249
|
-
description: "All active memory entries for a specific repository",
|
|
3250
|
-
mimeType: "application/json",
|
|
3251
|
-
annotations: { audience: ["assistant"], priority: 0.85 }
|
|
3252
|
-
},
|
|
3253
|
-
{
|
|
3254
|
-
uriTemplate: "repository://{name}/memories?search={search}&type={type}&tag={tag}",
|
|
3255
|
-
name: "Filtered Repository Memories",
|
|
3256
|
-
title: "Filtered Repository Memories",
|
|
3257
|
-
description: "Filter or search memories within a repository by keyword, type, or tag",
|
|
3258
|
-
mimeType: "application/json",
|
|
3259
|
-
annotations: { audience: ["assistant"], priority: 0.8 }
|
|
3260
|
-
},
|
|
3261
|
-
{
|
|
3262
|
-
uriTemplate: "memory://{id}",
|
|
3263
|
-
name: "Memory Detail",
|
|
3264
|
-
title: "Memory Detail",
|
|
3265
|
-
description: "Full content and statistics for a specific memory UUID",
|
|
3266
|
-
mimeType: "application/json",
|
|
3267
|
-
annotations: { audience: ["assistant"], priority: 0.75 }
|
|
3268
|
-
},
|
|
3269
|
-
// ── Tasks ────────────────────────────────────────────────────────────────
|
|
3270
|
-
{
|
|
3271
|
-
uriTemplate: "repository://{name}/tasks",
|
|
3272
|
-
name: "Repository Tasks",
|
|
3273
|
-
title: "Repository Tasks",
|
|
3274
|
-
description: "All active tasks for a specific repository",
|
|
3275
|
-
mimeType: "application/json",
|
|
3276
|
-
annotations: { audience: ["assistant"], priority: 0.9 }
|
|
3277
|
-
},
|
|
3278
|
-
{
|
|
3279
|
-
uriTemplate: "repository://{name}/tasks?status={status}&priority={priority}",
|
|
3280
|
-
name: "Filtered Repository Tasks",
|
|
3281
|
-
title: "Filtered Repository Tasks",
|
|
3282
|
-
description: "Filter tasks within a repository by status or priority level",
|
|
3283
|
-
mimeType: "application/json",
|
|
3284
|
-
annotations: { audience: ["assistant"], priority: 0.85 }
|
|
3285
|
-
},
|
|
3286
|
-
{
|
|
3287
|
-
uriTemplate: "task://{id}",
|
|
3288
|
-
name: "Task Detail",
|
|
3289
|
-
title: "Task Detail",
|
|
3290
|
-
description: "Full content and comments for a specific task UUID",
|
|
3291
|
-
mimeType: "application/json",
|
|
3292
|
-
annotations: { audience: ["assistant"], priority: 0.8 }
|
|
3293
|
-
},
|
|
3294
|
-
// ── Repository extras ────────────────────────────────────────────────────
|
|
3295
|
-
{
|
|
3296
|
-
uriTemplate: "repository://{name}/summary",
|
|
3297
|
-
name: "Repository Summary",
|
|
3298
|
-
title: "Repository Summary",
|
|
3299
|
-
description: "High-level architectural summary for a repository",
|
|
3300
|
-
mimeType: "text/plain",
|
|
3301
|
-
annotations: { audience: ["assistant"], priority: 0.95 }
|
|
3302
|
-
},
|
|
3303
|
-
{
|
|
3304
|
-
uriTemplate: "repository://{name}/actions",
|
|
3305
|
-
name: "Repository Actions",
|
|
3306
|
-
title: "Repository Actions",
|
|
3307
|
-
description: "Audit log of agent tool actions scoped to a repository",
|
|
3308
|
-
mimeType: "application/json",
|
|
3309
|
-
annotations: { audience: ["assistant"], priority: 0.6 }
|
|
3310
|
-
},
|
|
3311
|
-
// ── Action detail ────────────────────────────────────────────────────────
|
|
3312
|
-
{
|
|
3313
|
-
uriTemplate: "action://{id}",
|
|
3314
|
-
name: "Action Detail",
|
|
3315
|
-
title: "Action Detail",
|
|
3316
|
-
description: "Full details of a specific audit log entry by integer ID",
|
|
3317
|
-
mimeType: "application/json",
|
|
3318
|
-
annotations: { audience: ["assistant"], priority: 0.55 }
|
|
3319
|
-
}
|
|
3320
|
-
];
|
|
3321
|
-
return paginateEntries("resourceTemplates", templates, params);
|
|
3322
|
-
}
|
|
3323
|
-
function completeResourceArgument(resourceUri, argumentName, argumentValue, _contextArguments, dataSources) {
|
|
3324
|
-
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") {
|
|
3325
|
-
if (argumentName === "name") {
|
|
3326
|
-
return rankCompletionValues(dataSources.repos, argumentValue);
|
|
3327
|
-
}
|
|
3328
|
-
}
|
|
3329
|
-
if (resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}") {
|
|
3330
|
-
if (argumentName === "tag") {
|
|
3331
|
-
return rankCompletionValues(dataSources.tags, argumentValue);
|
|
3332
|
-
}
|
|
3333
|
-
}
|
|
3334
|
-
throw invalidCompletionParams(`Unknown resource template or argument: ${resourceUri} (${argumentName})`);
|
|
3335
|
-
}
|
|
3336
|
-
function readResource(uri, db, session) {
|
|
3337
|
-
logger.info("[Tool] resource.read", { uri });
|
|
3338
|
-
if (uri === "repository://index") {
|
|
3339
|
-
const repos = db.system.listRepoNavigation();
|
|
3340
|
-
const payload = JSON.stringify(repos, null, 2);
|
|
3341
|
-
return {
|
|
3342
|
-
contents: [
|
|
3343
|
-
{
|
|
3344
|
-
uri,
|
|
3345
|
-
mimeType: "application/json",
|
|
3346
|
-
text: payload,
|
|
3347
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
3348
|
-
annotations: {
|
|
3349
|
-
audience: ["assistant"],
|
|
3350
|
-
priority: 1,
|
|
3351
|
-
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
3352
|
-
}
|
|
3353
|
-
}
|
|
3354
|
-
]
|
|
3355
|
-
};
|
|
3356
|
-
}
|
|
3357
|
-
if (uri === "session://roots") {
|
|
3358
|
-
const payload = JSON.stringify({ roots: session?.roots ?? [] }, null, 2);
|
|
3359
|
-
return {
|
|
3360
|
-
contents: [
|
|
3361
|
-
{
|
|
3362
|
-
uri,
|
|
3363
|
-
mimeType: "application/json",
|
|
3364
|
-
text: payload,
|
|
3365
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
3366
|
-
annotations: {
|
|
3367
|
-
audience: ["assistant"],
|
|
3368
|
-
priority: 0.95,
|
|
3369
|
-
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
3370
|
-
}
|
|
3371
|
-
}
|
|
3372
|
-
]
|
|
3373
|
-
};
|
|
3374
|
-
}
|
|
3375
|
-
const memoryIdMatch = uri.match(/^memory:\/\/([0-9a-f-]{36})$/i);
|
|
3376
|
-
if (memoryIdMatch) {
|
|
3377
|
-
const id = memoryIdMatch[1];
|
|
3378
|
-
const entry = db.memories.getByIdWithStats(id);
|
|
3379
|
-
if (!entry) throw resourceNotFound(`Memory with ID ${id} not found.`, uri);
|
|
3380
|
-
const payload = JSON.stringify(entry, null, 2);
|
|
3381
|
-
return {
|
|
3382
|
-
contents: [
|
|
3383
|
-
{
|
|
3384
|
-
uri,
|
|
3385
|
-
mimeType: "application/json",
|
|
3386
|
-
text: payload,
|
|
3387
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
3388
|
-
annotations: {
|
|
3389
|
-
audience: ["assistant"],
|
|
3390
|
-
priority: 0.75,
|
|
3391
|
-
lastModified: entry.updated_at || entry.created_at
|
|
3392
|
-
}
|
|
3393
|
-
}
|
|
3394
|
-
]
|
|
3395
|
-
};
|
|
3396
|
-
}
|
|
3397
|
-
const taskIdMatch = uri.match(/^task:\/\/([0-9a-f-]{36})$/i);
|
|
3398
|
-
if (taskIdMatch) {
|
|
3399
|
-
const id = taskIdMatch[1];
|
|
3400
|
-
const task = db.tasks.getTaskById(id);
|
|
3401
|
-
if (!task) throw resourceNotFound(`Task with ID ${id} not found.`, uri);
|
|
3402
|
-
const payload = JSON.stringify(task, null, 2);
|
|
3403
|
-
return {
|
|
3404
|
-
contents: [
|
|
3405
|
-
{
|
|
3406
|
-
uri,
|
|
3407
|
-
mimeType: "application/json",
|
|
3408
|
-
text: payload,
|
|
3409
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
3410
|
-
annotations: {
|
|
3411
|
-
audience: ["assistant"],
|
|
3412
|
-
priority: 0.8,
|
|
3413
|
-
lastModified: task.updated_at || task.created_at
|
|
3414
|
-
}
|
|
3415
|
-
}
|
|
3416
|
-
]
|
|
3417
|
-
};
|
|
3418
|
-
}
|
|
3419
|
-
const repoBase = parseRepoUri(uri);
|
|
3420
|
-
if (repoBase) {
|
|
3421
|
-
const { name, path: repoPath, query } = repoBase;
|
|
3422
|
-
if (repoPath === "summary") {
|
|
3423
|
-
const summary = db.summaries.getSummary(name);
|
|
3424
|
-
const text = summary?.summary || `No summary available for repository: ${name}`;
|
|
3425
|
-
return {
|
|
3426
|
-
contents: [
|
|
3427
|
-
{
|
|
3428
|
-
uri,
|
|
3429
|
-
mimeType: "text/plain",
|
|
3430
|
-
text,
|
|
3431
|
-
size: Buffer.byteLength(text, "utf8"),
|
|
3432
|
-
annotations: {
|
|
3433
|
-
audience: ["assistant"],
|
|
3434
|
-
priority: 0.95,
|
|
3435
|
-
lastModified: summary?.updated_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
3436
|
-
}
|
|
3437
|
-
}
|
|
3438
|
-
]
|
|
3439
|
-
};
|
|
3440
|
-
}
|
|
3441
|
-
if (repoPath === "memories") {
|
|
3442
|
-
const search = query.get("search") || "";
|
|
3443
|
-
const type = query.get("type");
|
|
3444
|
-
const tag = query.get("tag");
|
|
3445
|
-
const result = db.memories.listMemoriesForDashboard({
|
|
3446
|
-
repo: name,
|
|
3447
|
-
type: type || void 0,
|
|
3448
|
-
tag: tag || void 0,
|
|
3449
|
-
search: search || void 0,
|
|
3450
|
-
limit: 50
|
|
3451
|
-
});
|
|
3452
|
-
const entries = result.items;
|
|
3453
|
-
const payload = JSON.stringify(entries, null, 2);
|
|
3454
|
-
return {
|
|
3455
|
-
contents: [
|
|
3456
|
-
{
|
|
3457
|
-
uri,
|
|
3458
|
-
mimeType: "application/json",
|
|
3459
|
-
text: payload,
|
|
3460
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
3461
|
-
annotations: {
|
|
3462
|
-
audience: ["assistant"],
|
|
3463
|
-
priority: 0.85,
|
|
3464
|
-
lastModified: deriveLastModifiedFromCollection(
|
|
3465
|
-
entries.map((e) => e.updated_at || e.created_at)
|
|
3466
|
-
)
|
|
3467
|
-
}
|
|
3468
|
-
}
|
|
3469
|
-
]
|
|
3470
|
-
};
|
|
3471
|
-
}
|
|
3472
|
-
if (repoPath === "tasks") {
|
|
3473
|
-
const status = query.get("status");
|
|
3474
|
-
const priority = query.get("priority");
|
|
3475
|
-
let tasks;
|
|
3476
|
-
if (status && status !== "all") {
|
|
3477
|
-
const statuses = status.split(",").map((s) => s.trim());
|
|
3478
|
-
tasks = db.tasks.getTasksByMultipleStatuses(name, statuses);
|
|
3479
|
-
} else {
|
|
3480
|
-
tasks = db.tasks.getTasksByMultipleStatuses(name, ["backlog", "pending", "in_progress", "blocked"]);
|
|
3481
|
-
}
|
|
3482
|
-
if (priority) {
|
|
3483
|
-
const p = Number(priority);
|
|
3484
|
-
if (!isNaN(p)) {
|
|
3485
|
-
tasks = tasks.filter((t) => t.priority === p);
|
|
3486
|
-
}
|
|
3487
|
-
}
|
|
3488
|
-
const payload = JSON.stringify(tasks, null, 2);
|
|
3489
|
-
return {
|
|
3490
|
-
contents: [
|
|
3491
|
-
{
|
|
3492
|
-
uri,
|
|
3493
|
-
mimeType: "application/json",
|
|
3494
|
-
text: payload,
|
|
3495
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
3496
|
-
annotations: {
|
|
3497
|
-
audience: ["assistant"],
|
|
3498
|
-
priority: 0.9,
|
|
3499
|
-
lastModified: deriveLastModifiedFromCollection(tasks.map((t) => t.updated_at))
|
|
3500
|
-
}
|
|
3501
|
-
}
|
|
3502
|
-
]
|
|
3503
|
-
};
|
|
3504
|
-
}
|
|
3505
|
-
if (repoPath === "actions") {
|
|
3506
|
-
const actions = db.actions.getRecentActions(name, 100);
|
|
3507
|
-
const payload = JSON.stringify(actions, null, 2);
|
|
3508
|
-
return {
|
|
3509
|
-
contents: [
|
|
3510
|
-
{
|
|
3511
|
-
uri,
|
|
3512
|
-
mimeType: "application/json",
|
|
3513
|
-
text: payload,
|
|
3514
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
3515
|
-
annotations: {
|
|
3516
|
-
audience: ["assistant"],
|
|
3517
|
-
priority: 0.6,
|
|
3518
|
-
lastModified: deriveLastModifiedFromCollection(actions.map((a) => a.created_at))
|
|
3519
|
-
}
|
|
3520
|
-
}
|
|
3521
|
-
]
|
|
3522
|
-
};
|
|
3523
|
-
}
|
|
3524
|
-
}
|
|
3525
|
-
const actionIdMatch = uri.match(/^action:\/\/(\d+)$/);
|
|
3526
|
-
if (actionIdMatch) {
|
|
3527
|
-
const id = Number(actionIdMatch[1]);
|
|
3528
|
-
const action = db.actions.getActionById(id);
|
|
3529
|
-
if (!action) throw resourceNotFound(`Action with ID ${id} not found.`, uri);
|
|
3530
|
-
const payload = JSON.stringify(action, null, 2);
|
|
3531
|
-
return {
|
|
3532
|
-
contents: [
|
|
3533
|
-
{
|
|
3534
|
-
uri,
|
|
3535
|
-
mimeType: "application/json",
|
|
3536
|
-
text: payload,
|
|
3537
|
-
size: Buffer.byteLength(payload, "utf8"),
|
|
3538
|
-
annotations: {
|
|
3539
|
-
audience: ["assistant"],
|
|
3540
|
-
priority: 0.55,
|
|
3541
|
-
lastModified: action.created_at
|
|
3542
|
-
}
|
|
3543
|
-
}
|
|
3544
|
-
]
|
|
3545
|
-
};
|
|
3546
|
-
}
|
|
3547
|
-
throw resourceNotFound(`Unknown resource URI: ${uri}`, uri);
|
|
3548
|
-
}
|
|
3549
|
-
function parseRepoUri(uri) {
|
|
3550
|
-
const prefix = "repository://";
|
|
3551
|
-
if (!uri.startsWith(prefix)) return null;
|
|
3552
|
-
const rest = uri.slice(prefix.length);
|
|
3553
|
-
const queryStart = rest.indexOf("?");
|
|
3554
|
-
const withoutQuery = queryStart === -1 ? rest : rest.slice(0, queryStart);
|
|
3555
|
-
const queryString = queryStart === -1 ? "" : rest.slice(queryStart + 1);
|
|
3556
|
-
const slashIdx = withoutQuery.indexOf("/");
|
|
3557
|
-
if (slashIdx === -1) return null;
|
|
3558
|
-
const name = withoutQuery.slice(0, slashIdx);
|
|
3559
|
-
const path6 = withoutQuery.slice(slashIdx + 1);
|
|
3560
|
-
if (!name || !path6) return null;
|
|
3561
|
-
return { name, path: path6, query: new URLSearchParams(queryString) };
|
|
3562
|
-
}
|
|
3563
|
-
function paginateEntries(key, entries, params) {
|
|
3564
|
-
const limit = normalizeLimit(params?.limit);
|
|
3565
|
-
const offset = decodeCursor(params?.cursor);
|
|
3566
|
-
const sliced = entries.slice(offset, offset + limit);
|
|
3567
|
-
const nextOffset = offset + sliced.length;
|
|
3568
|
-
return {
|
|
3569
|
-
[key]: sliced,
|
|
3570
|
-
nextCursor: nextOffset < entries.length ? encodeCursor(nextOffset) : void 0
|
|
3571
|
-
};
|
|
3572
|
-
}
|
|
3573
|
-
function normalizeLimit(limit) {
|
|
3574
|
-
if (typeof limit !== "number" || !Number.isFinite(limit)) {
|
|
3575
|
-
return DEFAULT_PAGE_SIZE;
|
|
3576
|
-
}
|
|
3577
|
-
return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
|
|
3578
|
-
}
|
|
3579
|
-
function deriveLastModifiedFromCollection(values) {
|
|
3580
|
-
const normalized = values.filter((value) => typeof value === "string" && value.length > 0);
|
|
3581
|
-
return normalized.sort().at(-1) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3582
|
-
}
|
|
3583
|
-
function resourceNotFound(message, uri) {
|
|
3584
|
-
const error = new Error(message);
|
|
3585
|
-
error.code = -32002;
|
|
3586
|
-
error.data = { uri };
|
|
3587
|
-
return error;
|
|
3588
|
-
}
|
|
3589
|
-
function invalidCompletionParams(message) {
|
|
3590
|
-
const error = new Error(message);
|
|
3591
|
-
error.code = -32602;
|
|
3592
|
-
return error;
|
|
3593
|
-
}
|
|
3594
|
-
|
|
3595
|
-
// src/mcp/prompts/registry.ts
|
|
3596
|
-
function createPromptDefinition(loaded) {
|
|
3597
|
-
return {
|
|
3598
|
-
name: loaded.name,
|
|
3599
|
-
description: loaded.description,
|
|
3600
|
-
arguments: loaded.arguments,
|
|
3601
|
-
agent: loaded.agent,
|
|
3602
|
-
messages: [
|
|
3603
|
-
{
|
|
3604
|
-
role: "user",
|
|
3605
|
-
content: {
|
|
3606
|
-
type: "text",
|
|
3607
|
-
text: loaded.content
|
|
3608
|
-
}
|
|
3609
|
-
}
|
|
3610
|
-
]
|
|
3611
|
-
};
|
|
3612
|
-
}
|
|
3613
|
-
var PROMPTS = {};
|
|
3614
|
-
var promptFiles = listPromptFiles();
|
|
3615
|
-
for (const name of promptFiles) {
|
|
3616
|
-
try {
|
|
3617
|
-
PROMPTS[name] = createPromptDefinition(loadPromptFromMarkdown(name));
|
|
3618
|
-
} catch (e) {
|
|
3619
|
-
logger.warn(`Failed to load prompt ${name}: ${e}`);
|
|
3620
|
-
}
|
|
3621
|
-
}
|
|
3622
|
-
async function listPrompts(db, session, params) {
|
|
3623
|
-
const allPrompts = Object.values(PROMPTS).map((p) => ({
|
|
3624
|
-
name: p.name,
|
|
3625
|
-
description: p.description,
|
|
3626
|
-
arguments: p.arguments,
|
|
3627
|
-
metadata: p.agent ? { agent: p.agent } : void 0
|
|
3628
|
-
}));
|
|
3629
|
-
const rawLimit = typeof params?.limit === "number" && Number.isInteger(params?.limit) ? params.limit : 25;
|
|
3630
|
-
const limit = Math.max(1, Math.min(100, Math.trunc(rawLimit)));
|
|
3631
|
-
const offset = decodeCursor(params?.cursor);
|
|
3632
|
-
const sliced = allPrompts.slice(offset, offset + limit);
|
|
3633
|
-
const nextOffset = offset + sliced.length;
|
|
3634
|
-
return {
|
|
3635
|
-
prompts: sliced,
|
|
3636
|
-
nextCursor: nextOffset < allPrompts.length ? encodeCursor(nextOffset) : void 0
|
|
3637
|
-
};
|
|
3638
|
-
}
|
|
3639
|
-
async function getPrompt(name, args = {}, db, session) {
|
|
3640
|
-
const prompt = PROMPTS[name];
|
|
3641
|
-
if (!prompt) {
|
|
3642
|
-
throw new Error(`Prompt not found: ${name}`);
|
|
3643
|
-
}
|
|
3644
|
-
const inferredRepo = inferRepoFromSession(session);
|
|
3645
|
-
const messages = prompt.messages.map((m) => {
|
|
3646
|
-
let text = m.content.text;
|
|
3647
|
-
for (const [key, value] of Object.entries(args)) {
|
|
3648
|
-
text = text.replace(new RegExp(`\\{{${key}\\}}`, "g"), value);
|
|
3649
|
-
}
|
|
3650
|
-
text = text.replace(/{{current_repo}}/g, inferredRepo || "unknown-repo");
|
|
3651
|
-
return {
|
|
3652
|
-
...m,
|
|
3653
|
-
content: {
|
|
3654
|
-
...m.content,
|
|
3655
|
-
text
|
|
3656
|
-
}
|
|
3657
|
-
};
|
|
3658
|
-
});
|
|
3659
|
-
return {
|
|
3660
|
-
description: prompt.description,
|
|
3661
|
-
messages,
|
|
3662
|
-
metadata: prompt.agent ? { agent: prompt.agent } : void 0
|
|
3663
|
-
};
|
|
3664
|
-
}
|
|
3665
|
-
async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
|
|
3666
|
-
void name;
|
|
3667
|
-
void contextArguments;
|
|
3668
|
-
if (argName === "task_id") {
|
|
3669
|
-
const values = dataSources.tasks.map((t) => t.id);
|
|
3670
|
-
return rankCompletionValues(values, value);
|
|
3671
|
-
}
|
|
3672
|
-
return [];
|
|
3153
|
+
return void 0;
|
|
3673
3154
|
}
|
|
3674
3155
|
|
|
3675
3156
|
// src/mcp/tools/schemas.ts
|
|
@@ -5241,100 +4722,619 @@ var TOOL_DEFINITIONS = [
|
|
|
5241
4722
|
destructiveHint: false,
|
|
5242
4723
|
openWorldHint: false
|
|
5243
4724
|
},
|
|
5244
|
-
inputSchema: {
|
|
5245
|
-
type: "object",
|
|
5246
|
-
properties: {
|
|
5247
|
-
id: { type: "string", description: "Standard ID to update" },
|
|
5248
|
-
name: { type: "string", minLength: 3, maxLength: 255 },
|
|
5249
|
-
content: { type: "string", minLength: 10 },
|
|
5250
|
-
parent_id: { type: "string", format: "uuid", nullable: true },
|
|
5251
|
-
context: { type: "string" },
|
|
5252
|
-
version: { type: "string" },
|
|
5253
|
-
language: { type: "string" },
|
|
5254
|
-
stack: { type: "array", items: { type: "string" } },
|
|
5255
|
-
repo: { type: "string" },
|
|
5256
|
-
is_global: { type: "boolean" },
|
|
5257
|
-
tags: { type: "array", items: { type: "string" } },
|
|
5258
|
-
metadata: { type: "object" },
|
|
5259
|
-
agent: { type: "string" },
|
|
5260
|
-
model: { type: "string" },
|
|
5261
|
-
structured: { type: "boolean", default: false }
|
|
5262
|
-
},
|
|
5263
|
-
required: ["id"]
|
|
4725
|
+
inputSchema: {
|
|
4726
|
+
type: "object",
|
|
4727
|
+
properties: {
|
|
4728
|
+
id: { type: "string", description: "Standard ID to update" },
|
|
4729
|
+
name: { type: "string", minLength: 3, maxLength: 255 },
|
|
4730
|
+
content: { type: "string", minLength: 10 },
|
|
4731
|
+
parent_id: { type: "string", format: "uuid", nullable: true },
|
|
4732
|
+
context: { type: "string" },
|
|
4733
|
+
version: { type: "string" },
|
|
4734
|
+
language: { type: "string" },
|
|
4735
|
+
stack: { type: "array", items: { type: "string" } },
|
|
4736
|
+
repo: { type: "string" },
|
|
4737
|
+
is_global: { type: "boolean" },
|
|
4738
|
+
tags: { type: "array", items: { type: "string" } },
|
|
4739
|
+
metadata: { type: "object" },
|
|
4740
|
+
agent: { type: "string" },
|
|
4741
|
+
model: { type: "string" },
|
|
4742
|
+
structured: { type: "boolean", default: false }
|
|
4743
|
+
},
|
|
4744
|
+
required: ["id"]
|
|
4745
|
+
},
|
|
4746
|
+
outputSchema: {
|
|
4747
|
+
type: "object",
|
|
4748
|
+
properties: {
|
|
4749
|
+
success: { type: "boolean" },
|
|
4750
|
+
id: { type: "string" },
|
|
4751
|
+
updatedFields: { type: "array", items: { type: "string" } }
|
|
4752
|
+
},
|
|
4753
|
+
required: ["success", "id", "updatedFields"]
|
|
4754
|
+
}
|
|
4755
|
+
},
|
|
4756
|
+
{
|
|
4757
|
+
name: "standard-search",
|
|
4758
|
+
title: "Standard Search",
|
|
4759
|
+
description: "MANDATORY PRE-IMPLEMENTATION CHECK: Call before any code edit, test edit, refactor, migration, or implementation decision to find applicable coding standards. Returns a compact pointer table; use `standard-detail` for relevant results. If no relevant standards are returned, continue and state that no applicable standards were found.",
|
|
4760
|
+
annotations: {
|
|
4761
|
+
readOnlyHint: true,
|
|
4762
|
+
idempotentHint: true,
|
|
4763
|
+
openWorldHint: false
|
|
4764
|
+
},
|
|
4765
|
+
inputSchema: {
|
|
4766
|
+
type: "object",
|
|
4767
|
+
properties: {
|
|
4768
|
+
query: { type: "string", description: "Search query (optional, searches title/content)" },
|
|
4769
|
+
stack: {
|
|
4770
|
+
type: "array",
|
|
4771
|
+
items: { type: "string" },
|
|
4772
|
+
description: "Technology stack to filter by (e.g., ['react', 'nextjs'])"
|
|
4773
|
+
},
|
|
4774
|
+
tags: {
|
|
4775
|
+
type: "array",
|
|
4776
|
+
items: { type: "string" },
|
|
4777
|
+
description: "Tag filter"
|
|
4778
|
+
},
|
|
4779
|
+
language: { type: "string", description: "Programming language filter" },
|
|
4780
|
+
context: { type: "string", description: "Context/category filter" },
|
|
4781
|
+
version: { type: "string", description: "Version filter" },
|
|
4782
|
+
repo: { type: "string", description: "Repository filter (optional)" },
|
|
4783
|
+
is_global: { type: "boolean", description: "Filter by global/repo-specific" },
|
|
4784
|
+
limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
|
|
4785
|
+
offset: { type: "number", minimum: 0, default: 0 },
|
|
4786
|
+
structured: { type: "boolean", default: false }
|
|
4787
|
+
},
|
|
4788
|
+
required: []
|
|
4789
|
+
},
|
|
4790
|
+
outputSchema: {
|
|
4791
|
+
type: "object",
|
|
4792
|
+
properties: {
|
|
4793
|
+
schema: { type: "string", enum: ["standard-search"] },
|
|
4794
|
+
query: { type: "string" },
|
|
4795
|
+
count: { type: "number", description: "Number of rows returned" },
|
|
4796
|
+
total: { type: "number", description: "Total number of matches before pagination" },
|
|
4797
|
+
offset: { type: "number" },
|
|
4798
|
+
limit: { type: "number" },
|
|
4799
|
+
results: {
|
|
4800
|
+
type: "object",
|
|
4801
|
+
properties: {
|
|
4802
|
+
columns: {
|
|
4803
|
+
type: "array",
|
|
4804
|
+
items: { type: "string" }
|
|
4805
|
+
},
|
|
4806
|
+
rows: {
|
|
4807
|
+
type: "array",
|
|
4808
|
+
items: { type: "array" },
|
|
4809
|
+
description: "Each row includes standard id and pointer metadata. Fetch full content via standard-detail."
|
|
4810
|
+
}
|
|
4811
|
+
},
|
|
4812
|
+
required: ["columns", "rows"]
|
|
4813
|
+
}
|
|
4814
|
+
},
|
|
4815
|
+
required: ["schema", "query", "count", "total", "offset", "limit", "results"]
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
];
|
|
4819
|
+
|
|
4820
|
+
// src/mcp/utils/pagination.ts
|
|
4821
|
+
function encodeCursor(offset) {
|
|
4822
|
+
return Buffer.from(String(offset), "utf8").toString("base64");
|
|
4823
|
+
}
|
|
4824
|
+
function decodeCursor(cursor) {
|
|
4825
|
+
if (cursor === void 0 || cursor === null || cursor === "") {
|
|
4826
|
+
return 0;
|
|
4827
|
+
}
|
|
4828
|
+
if (typeof cursor !== "string" || cursor.trim() === "") {
|
|
4829
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
4830
|
+
}
|
|
4831
|
+
let decoded;
|
|
4832
|
+
try {
|
|
4833
|
+
decoded = Buffer.from(cursor, "base64").toString("utf8");
|
|
4834
|
+
} catch {
|
|
4835
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
4836
|
+
}
|
|
4837
|
+
if (!/^\d+$/.test(decoded)) {
|
|
4838
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
4839
|
+
}
|
|
4840
|
+
const offset = Number.parseInt(decoded, 10);
|
|
4841
|
+
if (!Number.isFinite(offset) || offset < 0) {
|
|
4842
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
4843
|
+
}
|
|
4844
|
+
return offset;
|
|
4845
|
+
}
|
|
4846
|
+
function invalidPaginationParams(message) {
|
|
4847
|
+
const error = new Error(message);
|
|
4848
|
+
error.code = -32602;
|
|
4849
|
+
return error;
|
|
4850
|
+
}
|
|
4851
|
+
|
|
4852
|
+
// src/mcp/utils/completion.ts
|
|
4853
|
+
var MAX_COMPLETION_VALUES = 100;
|
|
4854
|
+
function rankCompletionValues(candidates, input) {
|
|
4855
|
+
const unique = [...new Set(candidates.filter(Boolean))];
|
|
4856
|
+
const needle = input.trim().toLowerCase();
|
|
4857
|
+
if (!needle) {
|
|
4858
|
+
return unique.slice(0, MAX_COMPLETION_VALUES);
|
|
4859
|
+
}
|
|
4860
|
+
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);
|
|
4861
|
+
}
|
|
4862
|
+
function scoreCompletionValue(value, needle) {
|
|
4863
|
+
const haystack = value.toLowerCase();
|
|
4864
|
+
if (haystack === needle) return 100;
|
|
4865
|
+
if (haystack.startsWith(needle)) return 75;
|
|
4866
|
+
if (haystack.includes(needle)) return 50;
|
|
4867
|
+
const compactNeedle = needle.replace(/[\s_-]+/g, "");
|
|
4868
|
+
const compactHaystack = haystack.replace(/[\s_-]+/g, "");
|
|
4869
|
+
if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
|
|
4870
|
+
return 0;
|
|
4871
|
+
}
|
|
4872
|
+
|
|
4873
|
+
// src/mcp/resources/index.ts
|
|
4874
|
+
var DEFAULT_PAGE_SIZE = 25;
|
|
4875
|
+
var MAX_PAGE_SIZE = 100;
|
|
4876
|
+
function listResources(session, params) {
|
|
4877
|
+
const resources = [
|
|
4878
|
+
{
|
|
4879
|
+
uri: "repository://index",
|
|
4880
|
+
name: "Repository Index",
|
|
4881
|
+
title: "Repository Index",
|
|
4882
|
+
description: "List of all known repositories with memory/task counts and last activity",
|
|
4883
|
+
mimeType: "application/json",
|
|
4884
|
+
annotations: {
|
|
4885
|
+
audience: ["assistant"],
|
|
4886
|
+
priority: 1,
|
|
4887
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
4888
|
+
}
|
|
4889
|
+
},
|
|
4890
|
+
{
|
|
4891
|
+
uri: "session://roots",
|
|
4892
|
+
name: "Session Roots",
|
|
4893
|
+
title: "Session Roots",
|
|
4894
|
+
description: session?.roots.length ? "Active workspace roots provided by the MCP client" : "No active workspace roots were provided by the MCP client",
|
|
4895
|
+
mimeType: "application/json",
|
|
4896
|
+
size: Buffer.byteLength(JSON.stringify({ roots: session?.roots ?? [] }), "utf8"),
|
|
4897
|
+
annotations: {
|
|
4898
|
+
audience: ["assistant"],
|
|
4899
|
+
priority: 0.95,
|
|
4900
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
4901
|
+
}
|
|
4902
|
+
}
|
|
4903
|
+
];
|
|
4904
|
+
return paginateEntries("resources", resources, params);
|
|
4905
|
+
}
|
|
4906
|
+
function listResourceTemplates(params) {
|
|
4907
|
+
const templates = [
|
|
4908
|
+
// ── Memory ──────────────────────────────────────────────────────────────
|
|
4909
|
+
{
|
|
4910
|
+
uriTemplate: "repository://{name}/memories",
|
|
4911
|
+
name: "Repository Memories",
|
|
4912
|
+
title: "Repository Memories",
|
|
4913
|
+
description: "All active memory entries for a specific repository",
|
|
4914
|
+
mimeType: "application/json",
|
|
4915
|
+
annotations: { audience: ["assistant"], priority: 0.85 }
|
|
4916
|
+
},
|
|
4917
|
+
{
|
|
4918
|
+
uriTemplate: "repository://{name}/memories?search={search}&type={type}&tag={tag}",
|
|
4919
|
+
name: "Filtered Repository Memories",
|
|
4920
|
+
title: "Filtered Repository Memories",
|
|
4921
|
+
description: "Filter or search memories within a repository by keyword, type, or tag",
|
|
4922
|
+
mimeType: "application/json",
|
|
4923
|
+
annotations: { audience: ["assistant"], priority: 0.8 }
|
|
4924
|
+
},
|
|
4925
|
+
{
|
|
4926
|
+
uriTemplate: "memory://{id}",
|
|
4927
|
+
name: "Memory Detail",
|
|
4928
|
+
title: "Memory Detail",
|
|
4929
|
+
description: "Full content and statistics for a specific memory UUID",
|
|
4930
|
+
mimeType: "application/json",
|
|
4931
|
+
annotations: { audience: ["assistant"], priority: 0.75 }
|
|
4932
|
+
},
|
|
4933
|
+
// ── Tasks ────────────────────────────────────────────────────────────────
|
|
4934
|
+
{
|
|
4935
|
+
uriTemplate: "repository://{name}/tasks",
|
|
4936
|
+
name: "Repository Tasks",
|
|
4937
|
+
title: "Repository Tasks",
|
|
4938
|
+
description: "All active tasks for a specific repository",
|
|
4939
|
+
mimeType: "application/json",
|
|
4940
|
+
annotations: { audience: ["assistant"], priority: 0.9 }
|
|
4941
|
+
},
|
|
4942
|
+
{
|
|
4943
|
+
uriTemplate: "repository://{name}/tasks?status={status}&priority={priority}",
|
|
4944
|
+
name: "Filtered Repository Tasks",
|
|
4945
|
+
title: "Filtered Repository Tasks",
|
|
4946
|
+
description: "Filter tasks within a repository by status or priority level",
|
|
4947
|
+
mimeType: "application/json",
|
|
4948
|
+
annotations: { audience: ["assistant"], priority: 0.85 }
|
|
4949
|
+
},
|
|
4950
|
+
{
|
|
4951
|
+
uriTemplate: "task://{id}",
|
|
4952
|
+
name: "Task Detail",
|
|
4953
|
+
title: "Task Detail",
|
|
4954
|
+
description: "Full content and comments for a specific task UUID",
|
|
4955
|
+
mimeType: "application/json",
|
|
4956
|
+
annotations: { audience: ["assistant"], priority: 0.8 }
|
|
5264
4957
|
},
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
}
|
|
5274
|
-
},
|
|
5275
|
-
{
|
|
5276
|
-
name: "standard-search",
|
|
5277
|
-
title: "Standard Search",
|
|
5278
|
-
description: "MANDATORY PRE-IMPLEMENTATION CHECK: Call before any code edit, test edit, refactor, migration, or implementation decision to find applicable coding standards. Returns a compact pointer table; use `standard-detail` for relevant results. If no relevant standards are returned, continue and state that no applicable standards were found.",
|
|
5279
|
-
annotations: {
|
|
5280
|
-
readOnlyHint: true,
|
|
5281
|
-
idempotentHint: true,
|
|
5282
|
-
openWorldHint: false
|
|
4958
|
+
// ── Repository extras ────────────────────────────────────────────────────
|
|
4959
|
+
{
|
|
4960
|
+
uriTemplate: "repository://{name}/summary",
|
|
4961
|
+
name: "Repository Summary",
|
|
4962
|
+
title: "Repository Summary",
|
|
4963
|
+
description: "High-level architectural summary for a repository",
|
|
4964
|
+
mimeType: "text/plain",
|
|
4965
|
+
annotations: { audience: ["assistant"], priority: 0.95 }
|
|
5283
4966
|
},
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
description: "Technology stack to filter by (e.g., ['react', 'nextjs'])"
|
|
5292
|
-
},
|
|
5293
|
-
tags: {
|
|
5294
|
-
type: "array",
|
|
5295
|
-
items: { type: "string" },
|
|
5296
|
-
description: "Tag filter"
|
|
5297
|
-
},
|
|
5298
|
-
language: { type: "string", description: "Programming language filter" },
|
|
5299
|
-
context: { type: "string", description: "Context/category filter" },
|
|
5300
|
-
version: { type: "string", description: "Version filter" },
|
|
5301
|
-
repo: { type: "string", description: "Repository filter (optional)" },
|
|
5302
|
-
is_global: { type: "boolean", description: "Filter by global/repo-specific" },
|
|
5303
|
-
limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
|
|
5304
|
-
offset: { type: "number", minimum: 0, default: 0 },
|
|
5305
|
-
structured: { type: "boolean", default: false }
|
|
5306
|
-
},
|
|
5307
|
-
required: []
|
|
4967
|
+
{
|
|
4968
|
+
uriTemplate: "repository://{name}/actions",
|
|
4969
|
+
name: "Repository Actions",
|
|
4970
|
+
title: "Repository Actions",
|
|
4971
|
+
description: "Audit log of agent tool actions scoped to a repository",
|
|
4972
|
+
mimeType: "application/json",
|
|
4973
|
+
annotations: { audience: ["assistant"], priority: 0.6 }
|
|
5308
4974
|
},
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
4975
|
+
// ── Action detail ────────────────────────────────────────────────────────
|
|
4976
|
+
{
|
|
4977
|
+
uriTemplate: "action://{id}",
|
|
4978
|
+
name: "Action Detail",
|
|
4979
|
+
title: "Action Detail",
|
|
4980
|
+
description: "Full details of a specific audit log entry by integer ID",
|
|
4981
|
+
mimeType: "application/json",
|
|
4982
|
+
annotations: { audience: ["assistant"], priority: 0.55 }
|
|
4983
|
+
}
|
|
4984
|
+
];
|
|
4985
|
+
return paginateEntries("resourceTemplates", templates, params);
|
|
4986
|
+
}
|
|
4987
|
+
function completeResourceArgument(resourceUri, argumentName, argumentValue, _contextArguments, dataSources) {
|
|
4988
|
+
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") {
|
|
4989
|
+
if (argumentName === "name") {
|
|
4990
|
+
return rankCompletionValues(dataSources.repos, argumentValue);
|
|
4991
|
+
}
|
|
4992
|
+
}
|
|
4993
|
+
if (resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}") {
|
|
4994
|
+
if (argumentName === "tag") {
|
|
4995
|
+
return rankCompletionValues(dataSources.tags, argumentValue);
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4998
|
+
throw invalidCompletionParams(`Unknown resource template or argument: ${resourceUri} (${argumentName})`);
|
|
4999
|
+
}
|
|
5000
|
+
function readResource(uri, db, session) {
|
|
5001
|
+
logger.info("[Tool] resource.read", { uri });
|
|
5002
|
+
if (uri === "repository://index") {
|
|
5003
|
+
const repos = db.system.listRepoNavigation();
|
|
5004
|
+
const payload = JSON.stringify(repos, null, 2);
|
|
5005
|
+
return {
|
|
5006
|
+
contents: [
|
|
5007
|
+
{
|
|
5008
|
+
uri,
|
|
5009
|
+
mimeType: "application/json",
|
|
5010
|
+
text: payload,
|
|
5011
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
5012
|
+
annotations: {
|
|
5013
|
+
audience: ["assistant"],
|
|
5014
|
+
priority: 1,
|
|
5015
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
5016
|
+
}
|
|
5017
|
+
}
|
|
5018
|
+
]
|
|
5019
|
+
};
|
|
5020
|
+
}
|
|
5021
|
+
if (uri === "session://roots") {
|
|
5022
|
+
const payload = JSON.stringify({ roots: session?.roots ?? [] }, null, 2);
|
|
5023
|
+
return {
|
|
5024
|
+
contents: [
|
|
5025
|
+
{
|
|
5026
|
+
uri,
|
|
5027
|
+
mimeType: "application/json",
|
|
5028
|
+
text: payload,
|
|
5029
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
5030
|
+
annotations: {
|
|
5031
|
+
audience: ["assistant"],
|
|
5032
|
+
priority: 0.95,
|
|
5033
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
]
|
|
5037
|
+
};
|
|
5038
|
+
}
|
|
5039
|
+
const memoryIdMatch = uri.match(/^memory:\/\/([0-9a-f-]{36})$/i);
|
|
5040
|
+
if (memoryIdMatch) {
|
|
5041
|
+
const id = memoryIdMatch[1];
|
|
5042
|
+
const entry = db.memories.getByIdWithStats(id);
|
|
5043
|
+
if (!entry) throw resourceNotFound(`Memory with ID ${id} not found.`, uri);
|
|
5044
|
+
const payload = JSON.stringify(entry, null, 2);
|
|
5045
|
+
return {
|
|
5046
|
+
contents: [
|
|
5047
|
+
{
|
|
5048
|
+
uri,
|
|
5049
|
+
mimeType: "application/json",
|
|
5050
|
+
text: payload,
|
|
5051
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
5052
|
+
annotations: {
|
|
5053
|
+
audience: ["assistant"],
|
|
5054
|
+
priority: 0.75,
|
|
5055
|
+
lastModified: entry.updated_at || entry.created_at
|
|
5056
|
+
}
|
|
5057
|
+
}
|
|
5058
|
+
]
|
|
5059
|
+
};
|
|
5060
|
+
}
|
|
5061
|
+
const taskIdMatch = uri.match(/^task:\/\/([0-9a-f-]{36})$/i);
|
|
5062
|
+
if (taskIdMatch) {
|
|
5063
|
+
const id = taskIdMatch[1];
|
|
5064
|
+
const task = db.tasks.getTaskById(id);
|
|
5065
|
+
if (!task) throw resourceNotFound(`Task with ID ${id} not found.`, uri);
|
|
5066
|
+
const payload = JSON.stringify(task, null, 2);
|
|
5067
|
+
return {
|
|
5068
|
+
contents: [
|
|
5069
|
+
{
|
|
5070
|
+
uri,
|
|
5071
|
+
mimeType: "application/json",
|
|
5072
|
+
text: payload,
|
|
5073
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
5074
|
+
annotations: {
|
|
5075
|
+
audience: ["assistant"],
|
|
5076
|
+
priority: 0.8,
|
|
5077
|
+
lastModified: task.updated_at || task.created_at
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
]
|
|
5081
|
+
};
|
|
5082
|
+
}
|
|
5083
|
+
const repoBase = parseRepoUri(uri);
|
|
5084
|
+
if (repoBase) {
|
|
5085
|
+
const { name, path: repoPath, query } = repoBase;
|
|
5086
|
+
if (repoPath === "summary") {
|
|
5087
|
+
const summary = db.summaries.getSummary(name);
|
|
5088
|
+
const text = summary?.summary || `No summary available for repository: ${name}`;
|
|
5089
|
+
return {
|
|
5090
|
+
contents: [
|
|
5091
|
+
{
|
|
5092
|
+
uri,
|
|
5093
|
+
mimeType: "text/plain",
|
|
5094
|
+
text,
|
|
5095
|
+
size: Buffer.byteLength(text, "utf8"),
|
|
5096
|
+
annotations: {
|
|
5097
|
+
audience: ["assistant"],
|
|
5098
|
+
priority: 0.95,
|
|
5099
|
+
lastModified: summary?.updated_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
5100
|
+
}
|
|
5101
|
+
}
|
|
5102
|
+
]
|
|
5103
|
+
};
|
|
5104
|
+
}
|
|
5105
|
+
if (repoPath === "memories") {
|
|
5106
|
+
const search = query.get("search") || "";
|
|
5107
|
+
const type = query.get("type");
|
|
5108
|
+
const tag = query.get("tag");
|
|
5109
|
+
const result = db.memories.listMemoriesForDashboard({
|
|
5110
|
+
repo: name,
|
|
5111
|
+
type: type || void 0,
|
|
5112
|
+
tag: tag || void 0,
|
|
5113
|
+
search: search || void 0,
|
|
5114
|
+
limit: 50
|
|
5115
|
+
});
|
|
5116
|
+
const entries = result.items;
|
|
5117
|
+
const payload = JSON.stringify(entries, null, 2);
|
|
5118
|
+
return {
|
|
5119
|
+
contents: [
|
|
5120
|
+
{
|
|
5121
|
+
uri,
|
|
5122
|
+
mimeType: "application/json",
|
|
5123
|
+
text: payload,
|
|
5124
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
5125
|
+
annotations: {
|
|
5126
|
+
audience: ["assistant"],
|
|
5127
|
+
priority: 0.85,
|
|
5128
|
+
lastModified: deriveLastModifiedFromCollection(
|
|
5129
|
+
entries.map((e) => e.updated_at || e.created_at)
|
|
5130
|
+
)
|
|
5329
5131
|
}
|
|
5330
|
-
}
|
|
5331
|
-
|
|
5132
|
+
}
|
|
5133
|
+
]
|
|
5134
|
+
};
|
|
5135
|
+
}
|
|
5136
|
+
if (repoPath === "tasks") {
|
|
5137
|
+
const status = query.get("status");
|
|
5138
|
+
const priority = query.get("priority");
|
|
5139
|
+
let tasks;
|
|
5140
|
+
if (status && status !== "all") {
|
|
5141
|
+
const statuses = status.split(",").map((s) => s.trim());
|
|
5142
|
+
tasks = db.tasks.getTasksByMultipleStatuses(name, statuses);
|
|
5143
|
+
} else {
|
|
5144
|
+
tasks = db.tasks.getTasksByMultipleStatuses(name, ["backlog", "pending", "in_progress", "blocked"]);
|
|
5145
|
+
}
|
|
5146
|
+
if (priority) {
|
|
5147
|
+
const p = Number(priority);
|
|
5148
|
+
if (!isNaN(p)) {
|
|
5149
|
+
tasks = tasks.filter((t) => t.priority === p);
|
|
5332
5150
|
}
|
|
5333
|
-
}
|
|
5334
|
-
|
|
5151
|
+
}
|
|
5152
|
+
const payload = JSON.stringify(tasks, null, 2);
|
|
5153
|
+
return {
|
|
5154
|
+
contents: [
|
|
5155
|
+
{
|
|
5156
|
+
uri,
|
|
5157
|
+
mimeType: "application/json",
|
|
5158
|
+
text: payload,
|
|
5159
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
5160
|
+
annotations: {
|
|
5161
|
+
audience: ["assistant"],
|
|
5162
|
+
priority: 0.9,
|
|
5163
|
+
lastModified: deriveLastModifiedFromCollection(tasks.map((t) => t.updated_at))
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
]
|
|
5167
|
+
};
|
|
5168
|
+
}
|
|
5169
|
+
if (repoPath === "actions") {
|
|
5170
|
+
const actions = db.actions.getRecentActions(name, 100);
|
|
5171
|
+
const payload = JSON.stringify(actions, null, 2);
|
|
5172
|
+
return {
|
|
5173
|
+
contents: [
|
|
5174
|
+
{
|
|
5175
|
+
uri,
|
|
5176
|
+
mimeType: "application/json",
|
|
5177
|
+
text: payload,
|
|
5178
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
5179
|
+
annotations: {
|
|
5180
|
+
audience: ["assistant"],
|
|
5181
|
+
priority: 0.6,
|
|
5182
|
+
lastModified: deriveLastModifiedFromCollection(actions.map((a) => a.created_at))
|
|
5183
|
+
}
|
|
5184
|
+
}
|
|
5185
|
+
]
|
|
5186
|
+
};
|
|
5335
5187
|
}
|
|
5336
5188
|
}
|
|
5337
|
-
|
|
5189
|
+
const actionIdMatch = uri.match(/^action:\/\/(\d+)$/);
|
|
5190
|
+
if (actionIdMatch) {
|
|
5191
|
+
const id = Number(actionIdMatch[1]);
|
|
5192
|
+
const action = db.actions.getActionById(id);
|
|
5193
|
+
if (!action) throw resourceNotFound(`Action with ID ${id} not found.`, uri);
|
|
5194
|
+
const payload = JSON.stringify(action, null, 2);
|
|
5195
|
+
return {
|
|
5196
|
+
contents: [
|
|
5197
|
+
{
|
|
5198
|
+
uri,
|
|
5199
|
+
mimeType: "application/json",
|
|
5200
|
+
text: payload,
|
|
5201
|
+
size: Buffer.byteLength(payload, "utf8"),
|
|
5202
|
+
annotations: {
|
|
5203
|
+
audience: ["assistant"],
|
|
5204
|
+
priority: 0.55,
|
|
5205
|
+
lastModified: action.created_at
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
]
|
|
5209
|
+
};
|
|
5210
|
+
}
|
|
5211
|
+
throw resourceNotFound(`Unknown resource URI: ${uri}`, uri);
|
|
5212
|
+
}
|
|
5213
|
+
function parseRepoUri(uri) {
|
|
5214
|
+
const prefix = "repository://";
|
|
5215
|
+
if (!uri.startsWith(prefix)) return null;
|
|
5216
|
+
const rest = uri.slice(prefix.length);
|
|
5217
|
+
const queryStart = rest.indexOf("?");
|
|
5218
|
+
const withoutQuery = queryStart === -1 ? rest : rest.slice(0, queryStart);
|
|
5219
|
+
const queryString = queryStart === -1 ? "" : rest.slice(queryStart + 1);
|
|
5220
|
+
const slashIdx = withoutQuery.indexOf("/");
|
|
5221
|
+
if (slashIdx === -1) return null;
|
|
5222
|
+
const name = withoutQuery.slice(0, slashIdx);
|
|
5223
|
+
const path6 = withoutQuery.slice(slashIdx + 1);
|
|
5224
|
+
if (!name || !path6) return null;
|
|
5225
|
+
return { name, path: path6, query: new URLSearchParams(queryString) };
|
|
5226
|
+
}
|
|
5227
|
+
function paginateEntries(key, entries, params) {
|
|
5228
|
+
const limit = normalizeLimit(params?.limit);
|
|
5229
|
+
const offset = decodeCursor(params?.cursor);
|
|
5230
|
+
const sliced = entries.slice(offset, offset + limit);
|
|
5231
|
+
const nextOffset = offset + sliced.length;
|
|
5232
|
+
return {
|
|
5233
|
+
[key]: sliced,
|
|
5234
|
+
nextCursor: nextOffset < entries.length ? encodeCursor(nextOffset) : void 0
|
|
5235
|
+
};
|
|
5236
|
+
}
|
|
5237
|
+
function normalizeLimit(limit) {
|
|
5238
|
+
if (typeof limit !== "number" || !Number.isFinite(limit)) {
|
|
5239
|
+
return DEFAULT_PAGE_SIZE;
|
|
5240
|
+
}
|
|
5241
|
+
return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
|
|
5242
|
+
}
|
|
5243
|
+
function deriveLastModifiedFromCollection(values) {
|
|
5244
|
+
const normalized = values.filter((value) => typeof value === "string" && value.length > 0);
|
|
5245
|
+
return normalized.sort().at(-1) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
5246
|
+
}
|
|
5247
|
+
function resourceNotFound(message, uri) {
|
|
5248
|
+
const error = new Error(message);
|
|
5249
|
+
error.code = -32002;
|
|
5250
|
+
error.data = { uri };
|
|
5251
|
+
return error;
|
|
5252
|
+
}
|
|
5253
|
+
function invalidCompletionParams(message) {
|
|
5254
|
+
const error = new Error(message);
|
|
5255
|
+
error.code = -32602;
|
|
5256
|
+
return error;
|
|
5257
|
+
}
|
|
5258
|
+
|
|
5259
|
+
// src/mcp/prompts/registry.ts
|
|
5260
|
+
function createPromptDefinition(loaded) {
|
|
5261
|
+
return {
|
|
5262
|
+
name: loaded.name,
|
|
5263
|
+
description: loaded.description,
|
|
5264
|
+
arguments: loaded.arguments,
|
|
5265
|
+
agent: loaded.agent,
|
|
5266
|
+
messages: [
|
|
5267
|
+
{
|
|
5268
|
+
role: "user",
|
|
5269
|
+
content: {
|
|
5270
|
+
type: "text",
|
|
5271
|
+
text: loaded.content
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
]
|
|
5275
|
+
};
|
|
5276
|
+
}
|
|
5277
|
+
var PROMPTS = {};
|
|
5278
|
+
var promptFiles = listPromptFiles();
|
|
5279
|
+
for (const name of promptFiles) {
|
|
5280
|
+
try {
|
|
5281
|
+
PROMPTS[name] = createPromptDefinition(loadPromptFromMarkdown(name));
|
|
5282
|
+
} catch (e) {
|
|
5283
|
+
logger.warn(`Failed to load prompt ${name}: ${e}`);
|
|
5284
|
+
}
|
|
5285
|
+
}
|
|
5286
|
+
async function listPrompts(db, session, params) {
|
|
5287
|
+
const allPrompts = Object.values(PROMPTS).map((p) => ({
|
|
5288
|
+
name: p.name,
|
|
5289
|
+
description: p.description,
|
|
5290
|
+
arguments: p.arguments,
|
|
5291
|
+
metadata: p.agent ? { agent: p.agent } : void 0
|
|
5292
|
+
}));
|
|
5293
|
+
const rawLimit = typeof params?.limit === "number" && Number.isInteger(params?.limit) ? params.limit : 25;
|
|
5294
|
+
const limit = Math.max(1, Math.min(100, Math.trunc(rawLimit)));
|
|
5295
|
+
const offset = decodeCursor(params?.cursor);
|
|
5296
|
+
const sliced = allPrompts.slice(offset, offset + limit);
|
|
5297
|
+
const nextOffset = offset + sliced.length;
|
|
5298
|
+
return {
|
|
5299
|
+
prompts: sliced,
|
|
5300
|
+
nextCursor: nextOffset < allPrompts.length ? encodeCursor(nextOffset) : void 0
|
|
5301
|
+
};
|
|
5302
|
+
}
|
|
5303
|
+
async function getPrompt(name, args = {}, db, session) {
|
|
5304
|
+
const prompt = PROMPTS[name];
|
|
5305
|
+
if (!prompt) {
|
|
5306
|
+
throw new Error(`Prompt not found: ${name}`);
|
|
5307
|
+
}
|
|
5308
|
+
const inferredRepo = inferRepoFromSession(session);
|
|
5309
|
+
const messages = prompt.messages.map((m) => {
|
|
5310
|
+
let text = m.content.text;
|
|
5311
|
+
for (const [key, value] of Object.entries(args)) {
|
|
5312
|
+
text = text.replace(new RegExp(`\\{{${key}\\}}`, "g"), value);
|
|
5313
|
+
}
|
|
5314
|
+
text = text.replace(/{{current_repo}}/g, inferredRepo || "unknown-repo");
|
|
5315
|
+
return {
|
|
5316
|
+
...m,
|
|
5317
|
+
content: {
|
|
5318
|
+
...m.content,
|
|
5319
|
+
text
|
|
5320
|
+
}
|
|
5321
|
+
};
|
|
5322
|
+
});
|
|
5323
|
+
return {
|
|
5324
|
+
description: prompt.description,
|
|
5325
|
+
messages,
|
|
5326
|
+
metadata: prompt.agent ? { agent: prompt.agent } : void 0
|
|
5327
|
+
};
|
|
5328
|
+
}
|
|
5329
|
+
async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
|
|
5330
|
+
void name;
|
|
5331
|
+
void contextArguments;
|
|
5332
|
+
if (argName === "task_id") {
|
|
5333
|
+
const values = dataSources.tasks.map((t) => t.id);
|
|
5334
|
+
return rankCompletionValues(values, value);
|
|
5335
|
+
}
|
|
5336
|
+
return [];
|
|
5337
|
+
}
|
|
5338
5338
|
|
|
5339
5339
|
// src/mcp/tools/standard.shared.ts
|
|
5340
5340
|
function toContextSlug(value) {
|
|
@@ -5354,31 +5354,17 @@ function buildStandardVectorText(standard) {
|
|
|
5354
5354
|
}
|
|
5355
5355
|
|
|
5356
5356
|
export {
|
|
5357
|
+
MCP_PROTOCOL_VERSION,
|
|
5358
|
+
CAPABILITIES,
|
|
5357
5359
|
logger,
|
|
5358
5360
|
setLogLevel,
|
|
5359
5361
|
getLogLevel,
|
|
5360
5362
|
addLogSink,
|
|
5361
5363
|
LOG_LEVEL_VALUES,
|
|
5362
5364
|
createFileSink,
|
|
5363
|
-
encodeCursor,
|
|
5364
|
-
decodeCursor,
|
|
5365
|
-
listResources,
|
|
5366
|
-
listResourceTemplates,
|
|
5367
|
-
completeResourceArgument,
|
|
5368
|
-
readResource,
|
|
5369
|
-
createSessionContext,
|
|
5370
|
-
updateSessionFromInitialize,
|
|
5371
|
-
updateSessionRoots,
|
|
5372
|
-
extractRootsFromResult,
|
|
5373
|
-
getFilesystemRoots,
|
|
5374
|
-
isPathWithinRoots,
|
|
5375
|
-
findContainingRoot,
|
|
5376
|
-
inferRepoFromSession,
|
|
5377
|
-
PROMPTS,
|
|
5378
|
-
listPrompts,
|
|
5379
|
-
getPrompt,
|
|
5380
|
-
completePromptArgument,
|
|
5381
5365
|
normalizeRepo,
|
|
5366
|
+
SQLiteStore,
|
|
5367
|
+
RealVectorStore,
|
|
5382
5368
|
MemoryStoreSchema,
|
|
5383
5369
|
MemoryUpdateSchema,
|
|
5384
5370
|
MemorySearchSchema,
|
|
@@ -5406,10 +5392,24 @@ export {
|
|
|
5406
5392
|
StandardUpdateSchema,
|
|
5407
5393
|
StandardSearchSchema,
|
|
5408
5394
|
TOOL_DEFINITIONS,
|
|
5395
|
+
encodeCursor,
|
|
5396
|
+
decodeCursor,
|
|
5397
|
+
listResources,
|
|
5398
|
+
listResourceTemplates,
|
|
5399
|
+
completeResourceArgument,
|
|
5400
|
+
readResource,
|
|
5401
|
+
createSessionContext,
|
|
5402
|
+
updateSessionFromInitialize,
|
|
5403
|
+
updateSessionRoots,
|
|
5404
|
+
extractRootsFromResult,
|
|
5405
|
+
getFilesystemRoots,
|
|
5406
|
+
isPathWithinRoots,
|
|
5407
|
+
findContainingRoot,
|
|
5408
|
+
inferRepoFromSession,
|
|
5409
|
+
PROMPTS,
|
|
5410
|
+
listPrompts,
|
|
5411
|
+
getPrompt,
|
|
5412
|
+
completePromptArgument,
|
|
5409
5413
|
toContextSlug,
|
|
5410
|
-
buildStandardVectorText
|
|
5411
|
-
SQLiteStore,
|
|
5412
|
-
RealVectorStore,
|
|
5413
|
-
MCP_PROTOCOL_VERSION,
|
|
5414
|
-
CAPABILITIES
|
|
5414
|
+
buildStandardVectorText
|
|
5415
5415
|
};
|