cuemap-mcp 0.7.1 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -17
- package/SKILL.md +647 -0
- package/build/index.js +743 -135
- package/build/job-status.js +44 -0
- package/build/recall-result.js +53 -0
- package/package.json +13 -6
package/build/index.js
CHANGED
|
@@ -8,106 +8,241 @@ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
|
8
8
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
9
9
|
const zod_1 = require("zod");
|
|
10
10
|
const cuemap_1 = __importDefault(require("cuemap"));
|
|
11
|
-
const
|
|
11
|
+
const embedded_1 = require("cuemap/embedded");
|
|
12
|
+
const node_buffer_1 = require("node:buffer");
|
|
13
|
+
const node_fs_1 = require("node:fs");
|
|
12
14
|
const path_1 = __importDefault(require("path"));
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
15
|
+
const crypto_1 = require("crypto");
|
|
16
|
+
const child_process_1 = require("child_process");
|
|
17
|
+
const job_status_js_1 = require("./job-status.js");
|
|
18
|
+
const recall_result_js_1 = require("./recall-result.js");
|
|
19
|
+
let embeddedEngine = null;
|
|
16
20
|
let CUEMAP_URL = process.env.CUEMAP_URL;
|
|
17
21
|
let client;
|
|
22
|
+
const observedJobActivity = new Map();
|
|
23
|
+
function git(cwd, args) {
|
|
24
|
+
const result = (0, child_process_1.spawnSync)("git", args, { cwd, encoding: "utf8", timeout: 1_500 });
|
|
25
|
+
return result.status === 0 ? result.stdout.trim() : "";
|
|
26
|
+
}
|
|
27
|
+
function canonicalRemote(remote) {
|
|
28
|
+
return remote
|
|
29
|
+
.replace(/^[^@\s]+@([^:]+):/, "ssh://$1/")
|
|
30
|
+
.replace(/:\/\/[^/@]+@/, "://")
|
|
31
|
+
.replace(/\.git$/, "")
|
|
32
|
+
.replace(/\/+$/, "")
|
|
33
|
+
.toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
function defaultProjectId(cwd) {
|
|
36
|
+
if (process.env.CUEMAP_PROJECT)
|
|
37
|
+
return process.env.CUEMAP_PROJECT;
|
|
38
|
+
const root = git(cwd, ["rev-parse", "--show-toplevel"]) || cwd;
|
|
39
|
+
const remote = canonicalRemote(git(root, ["remote", "get-url", "origin"]));
|
|
40
|
+
const identity = remote || root;
|
|
41
|
+
const slug = path_1.default.basename(root)
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[^a-z0-9_-]+/g, "-")
|
|
44
|
+
.replace(/^-+|-+$/g, "") || "workspace";
|
|
45
|
+
const digest = (0, crypto_1.createHash)("sha256").update(identity).digest("hex").slice(0, 10);
|
|
46
|
+
return `repo-${slug.slice(0, 40)}-${digest}`.slice(0, 64);
|
|
47
|
+
}
|
|
48
|
+
const DEFAULT_PROJECT = defaultProjectId(process.cwd());
|
|
18
49
|
const server = new mcp_js_1.McpServer({
|
|
19
50
|
name: "cuemap-mcp",
|
|
20
|
-
version: "
|
|
51
|
+
version: "0.7.3",
|
|
21
52
|
});
|
|
22
|
-
function getCuemapBinaryPath() {
|
|
23
|
-
if (process.env.CUEMAP_BIN)
|
|
24
|
-
return process.env.CUEMAP_BIN;
|
|
25
|
-
// Check optional dependencies dynamically
|
|
26
|
-
try {
|
|
27
|
-
const platform = os_1.default.platform();
|
|
28
|
-
const arch = os_1.default.arch();
|
|
29
|
-
const pkgName = `@cuemap-dev/engine-${platform}-${arch}`;
|
|
30
|
-
const resolved = require.resolve(`${pkgName}/package.json`);
|
|
31
|
-
const binPath = path_1.default.join(path_1.default.dirname(resolved), "bin", "cuemap");
|
|
32
|
-
if (fs_1.default.existsSync(binPath))
|
|
33
|
-
return binPath;
|
|
34
|
-
}
|
|
35
|
-
catch (e) {
|
|
36
|
-
// Ignored
|
|
37
|
-
}
|
|
38
|
-
// Fallback: Check locally built binary (for development)
|
|
39
|
-
const localPath = path_1.default.resolve(__dirname, "../../rust_engine/target/release/cuemap");
|
|
40
|
-
if (fs_1.default.existsSync(localPath))
|
|
41
|
-
return localPath;
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
53
|
async function startEngine() {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
const configuredPort = process.env.CUEMAP_PORT
|
|
55
|
+
? Number.parseInt(process.env.CUEMAP_PORT, 10)
|
|
56
|
+
: undefined;
|
|
57
|
+
if (configuredPort !== undefined && (!Number.isInteger(configuredPort) || configuredPort < 1 || configuredPort > 65535)) {
|
|
58
|
+
throw new Error("CUEMAP_PORT must be an integer between 1 and 65535");
|
|
48
59
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
embeddedEngine = await embedded_1.EmbeddedCueMap.start({
|
|
61
|
+
url: CUEMAP_URL,
|
|
62
|
+
binPath: process.env.CUEMAP_BIN,
|
|
63
|
+
port: configuredPort,
|
|
64
|
+
requiredCapabilities: [
|
|
65
|
+
"repository_ingestion_scope_v1",
|
|
66
|
+
"semantic_retrieval_v1",
|
|
67
|
+
"chunk_embeddings_v1",
|
|
68
|
+
"intent_classification_v1",
|
|
69
|
+
"intent_job_status_v1",
|
|
70
|
+
"project_lifecycle_v1",
|
|
71
|
+
"project_packages_v1",
|
|
72
|
+
"project_sync_v1",
|
|
73
|
+
],
|
|
74
|
+
configPath: process.env.CUEMAP_CONFIG_PATH,
|
|
75
|
+
apiKey: process.env.CUEMAP_API_KEY,
|
|
76
|
+
logger: (message) => console.error(message),
|
|
77
|
+
});
|
|
78
|
+
CUEMAP_URL = embeddedEngine.url;
|
|
79
|
+
}
|
|
80
|
+
async function shutdown(signal) {
|
|
81
|
+
try {
|
|
82
|
+
await embeddedEngine?.stop();
|
|
53
83
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
CUEMAP_URL = `http://127.0.0.1:${port}`;
|
|
57
|
-
const args = ["start", "--port", port];
|
|
58
|
-
if (process.env.CUEMAP_CONFIG_PATH) {
|
|
59
|
-
args.push("--config", process.env.CUEMAP_CONFIG_PATH);
|
|
60
|
-
console.error(`Using custom config at ${process.env.CUEMAP_CONFIG_PATH}`);
|
|
84
|
+
finally {
|
|
85
|
+
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
61
86
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
console.error("Failed to start embedded engine:", err);
|
|
69
|
-
});
|
|
70
|
-
engineProcess.on("exit", (code) => {
|
|
71
|
-
console.error(`Embedded engine exited with code ${code}`);
|
|
72
|
-
});
|
|
73
|
-
process.on("SIGINT", () => {
|
|
74
|
-
if (engineProcess)
|
|
75
|
-
engineProcess.kill("SIGINT");
|
|
76
|
-
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
function projectClient(project) {
|
|
89
|
+
return new cuemap_1.default({
|
|
90
|
+
url: CUEMAP_URL,
|
|
91
|
+
apiKey: process.env.CUEMAP_API_KEY,
|
|
92
|
+
projectId: project || DEFAULT_PROJECT,
|
|
77
93
|
});
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
94
|
+
}
|
|
95
|
+
function jsonToolResult(value) {
|
|
96
|
+
return {
|
|
97
|
+
content: [{
|
|
98
|
+
type: "text",
|
|
99
|
+
text: JSON.stringify(value, null, 2),
|
|
100
|
+
}],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function confirmationRequired(action) {
|
|
104
|
+
return {
|
|
105
|
+
content: [{
|
|
106
|
+
type: "text",
|
|
107
|
+
text: `No changes were made because ${action} requires confirmed: true after explicit user confirmation.`,
|
|
108
|
+
}],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function toolError(toolName, error) {
|
|
112
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
113
|
+
console.error(`Error executing ${toolName}`, error);
|
|
114
|
+
return {
|
|
115
|
+
content: [{
|
|
116
|
+
type: "text",
|
|
117
|
+
text: `Error executing ${toolName} tool: ${message || "Unknown error"}`,
|
|
118
|
+
}],
|
|
119
|
+
isError: true,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async function engineRequest(method, requestPath, body, project) {
|
|
123
|
+
const response = await engineRawRequest(method, requestPath, body === undefined ? undefined : JSON.stringify(body), project, "application/json");
|
|
124
|
+
return await response.json();
|
|
125
|
+
}
|
|
126
|
+
async function engineRawRequest(method, requestPath, body, project, contentType) {
|
|
127
|
+
if (!CUEMAP_URL)
|
|
128
|
+
throw new Error("CueMap engine URL is not available");
|
|
129
|
+
const response = await fetch(`${CUEMAP_URL}${requestPath}`, {
|
|
130
|
+
method,
|
|
131
|
+
headers: {
|
|
132
|
+
...(body === undefined || !contentType ? {} : { "content-type": contentType }),
|
|
133
|
+
...(process.env.CUEMAP_API_KEY ? { "X-API-Key": process.env.CUEMAP_API_KEY } : {}),
|
|
134
|
+
...(project ? { "X-Project-ID": project } : {}),
|
|
135
|
+
},
|
|
136
|
+
...(body === undefined ? {} : { body }),
|
|
82
137
|
});
|
|
83
|
-
|
|
84
|
-
|
|
138
|
+
if (!response.ok) {
|
|
139
|
+
const message = await response.text();
|
|
140
|
+
throw new Error(`CueMap returned HTTP ${response.status}: ${message}`);
|
|
141
|
+
}
|
|
142
|
+
return response;
|
|
85
143
|
}
|
|
86
144
|
async function main() {
|
|
87
145
|
await startEngine();
|
|
88
|
-
|
|
146
|
+
process.once("SIGINT", () => void shutdown("SIGINT"));
|
|
147
|
+
process.once("SIGTERM", () => void shutdown("SIGTERM"));
|
|
148
|
+
client = new cuemap_1.default({
|
|
149
|
+
url: CUEMAP_URL,
|
|
150
|
+
apiKey: process.env.CUEMAP_API_KEY,
|
|
151
|
+
projectId: DEFAULT_PROJECT,
|
|
152
|
+
});
|
|
153
|
+
server.registerTool("cuemap_init_preview", {
|
|
154
|
+
description: "Preview supported repository files without ingesting content. Call this before first-time repository initialization, present the grouped paths to the user, and ask them to confirm or adjust the selection before calling cuemap_init.",
|
|
155
|
+
inputSchema: zod_1.z.object({
|
|
156
|
+
path: zod_1.z.string().describe("Absolute path to the repository root."),
|
|
157
|
+
projectName: zod_1.z.string().optional().describe("Optional project ID. Defaults to the stable repository-scoped CueMap project ID."),
|
|
158
|
+
includedPaths: zod_1.z.array(zod_1.z.string()).optional().describe("Optional repository-relative files or folders to preview. Empty means every supported file allowed by ignore rules."),
|
|
159
|
+
ignoredPatterns: zod_1.z.array(zod_1.z.string()).optional().describe("Optional additional gitignore-style exclusion patterns."),
|
|
160
|
+
ignoredExtensions: zod_1.z.array(zod_1.z.string()).optional().describe("Optional additional excluded extensions without a leading dot."),
|
|
161
|
+
}),
|
|
162
|
+
}, async (args) => {
|
|
163
|
+
try {
|
|
164
|
+
const project = args.projectName || defaultProjectId(args.path);
|
|
165
|
+
const projects = await client.listProjects();
|
|
166
|
+
const projectIds = projects.map((item) => typeof item === "string" ? item : item.project_id);
|
|
167
|
+
let currentScope = null;
|
|
168
|
+
if (projectIds.includes(project)) {
|
|
169
|
+
try {
|
|
170
|
+
currentScope = await engineRequest("GET", `/projects/${encodeURIComponent(project)}/watch-dir`);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
currentScope = null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const preview = await engineRequest("POST", "/ingest/directory/preview", {
|
|
177
|
+
watch_dir: args.path,
|
|
178
|
+
included_paths: args.includedPaths,
|
|
179
|
+
ignored_patterns: args.ignoredPatterns,
|
|
180
|
+
ignored_extensions: args.ignoredExtensions,
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
content: [{
|
|
184
|
+
type: "text",
|
|
185
|
+
text: JSON.stringify({
|
|
186
|
+
project_id: project,
|
|
187
|
+
current_scope: currentScope,
|
|
188
|
+
selection_semantics: "included_paths are repository-relative files or folders; an empty list selects all supported files allowed by ignore rules",
|
|
189
|
+
preview,
|
|
190
|
+
}, null, 2),
|
|
191
|
+
}],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
console.error("Error previewing CueMap initialization", error);
|
|
196
|
+
return {
|
|
197
|
+
content: [{
|
|
198
|
+
type: "text",
|
|
199
|
+
text: `Error previewing repository ingestion: ${error?.message || "Unknown error"}`,
|
|
200
|
+
}],
|
|
201
|
+
isError: true,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
});
|
|
89
205
|
server.registerTool("cuemap_init", {
|
|
90
|
-
description: "
|
|
206
|
+
description: "Apply a user-confirmed repository ingestion scope and start CueMap's incremental filesystem watcher. Always call cuemap_init_preview first for a new repository and obtain explicit user confirmation before setting confirmed=true.",
|
|
91
207
|
inputSchema: zod_1.z.object({
|
|
92
|
-
path: zod_1.z.string().describe("Absolute path to the
|
|
93
|
-
projectName: zod_1.z.string().optional().describe("
|
|
208
|
+
path: zod_1.z.string().describe("Absolute path to the repository root."),
|
|
209
|
+
projectName: zod_1.z.string().optional().describe("Optional project ID. Defaults to the stable repository-scoped CueMap project ID."),
|
|
210
|
+
includedPaths: zod_1.z.array(zod_1.z.string()).optional().describe("User-approved repository-relative files or folders. Empty means every supported file allowed by ignore rules."),
|
|
211
|
+
ignoredPatterns: zod_1.z.array(zod_1.z.string()).optional().describe("Additional gitignore-style exclusion patterns approved by the user."),
|
|
212
|
+
ignoredExtensions: zod_1.z.array(zod_1.z.string()).optional().describe("Additional excluded extensions without a leading dot."),
|
|
213
|
+
confirmed: zod_1.z.boolean().describe("Must be true only after the user explicitly confirms the previewed ingestion scope."),
|
|
94
214
|
})
|
|
95
215
|
}, async (args) => {
|
|
96
216
|
try {
|
|
97
|
-
|
|
217
|
+
if (!args.confirmed) {
|
|
218
|
+
return {
|
|
219
|
+
content: [{ type: "text", text: "No repository files were ingested because the proposed scope was not confirmed." }],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const pName = args.projectName || defaultProjectId(args.path);
|
|
98
223
|
const projects = await client.listProjects();
|
|
99
224
|
const projectIds = projects.map((p) => typeof p === 'string' ? p : p.project_id);
|
|
100
225
|
if (!projectIds.includes(pName)) {
|
|
101
226
|
await client.createProject(pName);
|
|
102
227
|
}
|
|
103
|
-
await
|
|
228
|
+
await engineRequest("POST", `/projects/${encodeURIComponent(pName)}/watch-dir`, {
|
|
229
|
+
watch_dir: args.path,
|
|
230
|
+
included_paths: args.includedPaths,
|
|
231
|
+
ignored_patterns: args.ignoredPatterns,
|
|
232
|
+
ignored_extensions: args.ignoredExtensions,
|
|
233
|
+
});
|
|
104
234
|
console.error(`Started ingestion for project ${pName} at ${args.path}`);
|
|
235
|
+
observedJobActivity.set(pName, false);
|
|
105
236
|
// Polling for job completion
|
|
106
237
|
let isComplete = false;
|
|
107
238
|
let checks = 0;
|
|
239
|
+
let lastStatus = null;
|
|
108
240
|
while (!isComplete && checks < 60) {
|
|
109
241
|
const status = await client.jobsStatus(pName);
|
|
110
|
-
|
|
242
|
+
lastStatus = status;
|
|
243
|
+
const evaluation = (0, job_status_js_1.evaluateJobStatus)(status, observedJobActivity.get(pName));
|
|
244
|
+
observedJobActivity.set(pName, evaluation.observed_activity);
|
|
245
|
+
if (evaluation.verified_complete) {
|
|
111
246
|
isComplete = true;
|
|
112
247
|
break;
|
|
113
248
|
}
|
|
@@ -116,12 +251,18 @@ async function main() {
|
|
|
116
251
|
}
|
|
117
252
|
if (isComplete) {
|
|
118
253
|
return {
|
|
119
|
-
content: [{
|
|
254
|
+
content: [{
|
|
255
|
+
type: "text",
|
|
256
|
+
text: `Successfully initialized and ingested project ${pName}. CueMap observed ingestion activity, reached a terminal job phase, and completed memory intent annotation. The watcher will automatically ingest new or changed supported files within the approved scope. You can now use cuemap_recall.`,
|
|
257
|
+
}]
|
|
120
258
|
};
|
|
121
259
|
}
|
|
122
260
|
else {
|
|
123
261
|
return {
|
|
124
|
-
content: [{
|
|
262
|
+
content: [{
|
|
263
|
+
type: "text",
|
|
264
|
+
text: `Project ${pName} initialized with the approved scope, but ingestion has not reached a verified terminal state. Last job status: ${JSON.stringify(lastStatus)}. Call cuemap_status periodically until verified_complete is true. The watcher is active and partial results may be returned by cuemap_recall.`,
|
|
265
|
+
}]
|
|
125
266
|
};
|
|
126
267
|
}
|
|
127
268
|
}
|
|
@@ -134,34 +275,41 @@ async function main() {
|
|
|
134
275
|
}
|
|
135
276
|
});
|
|
136
277
|
server.registerTool("cuemap_add", {
|
|
137
|
-
description: "Store a natural-language memory in
|
|
278
|
+
description: "Store a natural-language memory in CueMap. Uses the repository-scoped default project unless one is supplied, creates it when needed, and applies deterministic cue extraction plus any cues and metadata.",
|
|
138
279
|
inputSchema: zod_1.z.object({
|
|
139
280
|
content: zod_1.z.string().min(1).describe("The natural-language memory content to store."),
|
|
140
|
-
project: zod_1.z.string().min(1).describe("
|
|
141
|
-
cues: zod_1.z.array(zod_1.z.string()).optional().describe("
|
|
281
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to a stable ID derived from the current Git repository."),
|
|
282
|
+
cues: zod_1.z.array(zod_1.z.string()).optional().describe("Normally omit: CueMap generates cues from content automatically. Optionally add deliberate reusable tags, e.g. type:conversation for an explicitly saved conversation."),
|
|
142
283
|
metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional().describe("Optional JSON metadata to store with the memory."),
|
|
143
284
|
source_key: zod_1.z.string().optional().describe("Optional stable source key for deterministic upsert/deduplication."),
|
|
144
|
-
|
|
285
|
+
event_time: zod_1.z.number().nonnegative().optional().describe("Optional original event timestamp as Unix seconds. Defaults to ingestion time."),
|
|
286
|
+
embedding: zod_1.z.array(zod_1.z.number()).nonempty().optional().describe("Optional precomputed memory embedding."),
|
|
145
287
|
disable_temporal_chunking: zod_1.z.boolean().optional().describe("Disable temporal chunking for this memory. Default is false."),
|
|
146
288
|
async_ingest: zod_1.z.boolean().optional().describe("Process ingestion in the background and return immediately. Default is false."),
|
|
147
289
|
})
|
|
148
290
|
}, async (args) => {
|
|
149
291
|
try {
|
|
292
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
150
293
|
const projects = await client.listProjects();
|
|
151
294
|
const projectIds = projects.map((p) => typeof p === "string" ? p : p.project_id);
|
|
152
|
-
if (!projectIds.includes(
|
|
153
|
-
await client.createProject(
|
|
295
|
+
if (!projectIds.includes(project)) {
|
|
296
|
+
await client.createProject(project);
|
|
154
297
|
}
|
|
155
|
-
const projectClient = new cuemap_1.default({
|
|
298
|
+
const projectClient = new cuemap_1.default({
|
|
299
|
+
url: CUEMAP_URL,
|
|
300
|
+
apiKey: process.env.CUEMAP_API_KEY,
|
|
301
|
+
projectId: project,
|
|
302
|
+
});
|
|
156
303
|
const memoryId = await projectClient.add(args.content, args.cues || [], args.metadata, args.disable_temporal_chunking || false, {
|
|
157
304
|
sourceKey: args.source_key,
|
|
158
|
-
|
|
305
|
+
eventTime: args.event_time,
|
|
306
|
+
embedding: args.embedding,
|
|
159
307
|
asyncIngest: args.async_ingest,
|
|
160
308
|
});
|
|
161
309
|
return {
|
|
162
310
|
content: [{
|
|
163
311
|
type: "text",
|
|
164
|
-
text: `Stored memory ${memoryId} in CueMap project ${
|
|
312
|
+
text: `Stored memory ${memoryId} in CueMap project ${project}.`,
|
|
165
313
|
}],
|
|
166
314
|
};
|
|
167
315
|
}
|
|
@@ -176,23 +324,525 @@ async function main() {
|
|
|
176
324
|
};
|
|
177
325
|
}
|
|
178
326
|
});
|
|
327
|
+
server.registerTool("cuemap_intent_classify", {
|
|
328
|
+
description: "Classify text with CueMap's local intent model and return recall/memory eligibility signals. Scores are ranking signals, not calibrated probabilities.",
|
|
329
|
+
inputSchema: zod_1.z.object({
|
|
330
|
+
text: zod_1.z.string().min(1),
|
|
331
|
+
target: zod_1.z.enum(["query", "memory"]).optional().describe("Classification target. Default is query."),
|
|
332
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
333
|
+
}),
|
|
334
|
+
}, async (args) => {
|
|
335
|
+
try {
|
|
336
|
+
return jsonToolResult(await projectClient(args.project).classifyIntent(args.text, args.target || "query"));
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
return toolError("cuemap_intent_classify", error);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
server.registerTool("cuemap_status", {
|
|
343
|
+
description: "Check CueMap background ingestion progress for a project. After cuemap_init, poll this tool until verified_complete is true. An initial idle status with 0/0 writes is not proof that ingestion completed.",
|
|
344
|
+
inputSchema: zod_1.z.object({
|
|
345
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the stable repository-scoped CueMap project ID."),
|
|
346
|
+
}),
|
|
347
|
+
}, async (args) => {
|
|
348
|
+
try {
|
|
349
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
350
|
+
const status = await client.jobsStatus(project);
|
|
351
|
+
const evaluation = (0, job_status_js_1.evaluateJobStatus)(status, observedJobActivity.get(project));
|
|
352
|
+
observedJobActivity.set(project, evaluation.observed_activity);
|
|
353
|
+
return {
|
|
354
|
+
content: [{
|
|
355
|
+
type: "text",
|
|
356
|
+
text: JSON.stringify({
|
|
357
|
+
project_id: project,
|
|
358
|
+
...status,
|
|
359
|
+
...evaluation,
|
|
360
|
+
}, null, 2),
|
|
361
|
+
}],
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
console.error("Error checking CueMap status", error);
|
|
366
|
+
return {
|
|
367
|
+
content: [{
|
|
368
|
+
type: "text",
|
|
369
|
+
text: `Error executing cuemap_status tool: ${error?.message || "Unknown error"}`,
|
|
370
|
+
}],
|
|
371
|
+
isError: true,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
server.registerTool("cuemap_projects", {
|
|
376
|
+
description: "List CueMap projects, summary metadata, and whether each project is currently loaded in RAM.",
|
|
377
|
+
inputSchema: zod_1.z.object({}),
|
|
378
|
+
}, async () => {
|
|
379
|
+
try {
|
|
380
|
+
return jsonToolResult(await client.listProjects());
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
return toolError("cuemap_projects", error);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
server.registerTool("cuemap_project_save", {
|
|
387
|
+
description: "Persist the current state of a CueMap project without unloading it. Package operations save automatically; use this only when an explicit durable checkpoint is useful.",
|
|
388
|
+
inputSchema: zod_1.z.object({
|
|
389
|
+
project: zod_1.z.string().min(1).optional().describe("Project ID to save. Defaults to the repository-scoped project."),
|
|
390
|
+
}),
|
|
391
|
+
}, async (args) => {
|
|
392
|
+
try {
|
|
393
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
394
|
+
return jsonToolResult(await engineRequest("POST", `/projects/${encodeURIComponent(project)}/save`, undefined, project));
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
return toolError("cuemap_project_save", error);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
server.registerTool("cuemap_project_load", {
|
|
401
|
+
description: "Load a persisted CueMap project into RAM before a latency-sensitive operation. Normal project requests load automatically, so use this for explicit warm-up.",
|
|
402
|
+
inputSchema: zod_1.z.object({
|
|
403
|
+
project: zod_1.z.string().min(1).optional().describe("Project ID to load. Defaults to the repository-scoped project."),
|
|
404
|
+
}),
|
|
405
|
+
}, async (args) => {
|
|
406
|
+
try {
|
|
407
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
408
|
+
return jsonToolResult(await engineRequest("POST", `/projects/${encodeURIComponent(project)}/load`, undefined, project));
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
return toolError("cuemap_project_load", error);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
server.registerTool("cuemap_project_unload", {
|
|
415
|
+
description: "Persist and unload a CueMap project from RAM to reduce memory use. Use only when the user explicitly asks to unload or free inactive project memory; active projects return a retryable busy error.",
|
|
416
|
+
inputSchema: zod_1.z.object({
|
|
417
|
+
project: zod_1.z.string().min(1).optional().describe("Project ID to unload. Defaults to the repository-scoped project."),
|
|
418
|
+
}),
|
|
419
|
+
}, async (args) => {
|
|
420
|
+
try {
|
|
421
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
422
|
+
return jsonToolResult(await engineRequest("POST", `/projects/${encodeURIComponent(project)}/unload`, undefined, project));
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
return toolError("cuemap_project_unload", error);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
server.registerTool("cuemap_project_pack", {
|
|
429
|
+
description: "Write a ready-to-query .cuemap package for one project to a local file. The package contains sensitive project content; use only after the user explicitly approves the exact output path.",
|
|
430
|
+
inputSchema: zod_1.z.object({
|
|
431
|
+
project: zod_1.z.string().min(1).optional().describe("Project ID to package. Defaults to the repository-scoped project."),
|
|
432
|
+
output_path: zod_1.z.string().min(1).describe("Absolute local path for the .cuemap file."),
|
|
433
|
+
overwrite: zod_1.z.boolean().optional().describe("Replace an existing output file. Default is false."),
|
|
434
|
+
confirmed: zod_1.z.boolean().optional().describe("Must be true after explicit user approval of the output path and any overwrite."),
|
|
435
|
+
}),
|
|
436
|
+
}, async (args) => {
|
|
437
|
+
if (!args.confirmed)
|
|
438
|
+
return confirmationRequired("project packaging");
|
|
439
|
+
try {
|
|
440
|
+
if (!path_1.default.isAbsolute(args.output_path)) {
|
|
441
|
+
throw new Error("output_path must be absolute");
|
|
442
|
+
}
|
|
443
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
444
|
+
const response = await engineRawRequest("POST", `/projects/${encodeURIComponent(project)}/pack`, undefined, project);
|
|
445
|
+
const packageData = Buffer.from(await response.arrayBuffer());
|
|
446
|
+
(0, node_fs_1.writeFileSync)(args.output_path, packageData, {
|
|
447
|
+
flag: args.overwrite ? "w" : "wx",
|
|
448
|
+
});
|
|
449
|
+
return jsonToolResult({
|
|
450
|
+
status: "packed",
|
|
451
|
+
project_id: project,
|
|
452
|
+
output_path: args.output_path,
|
|
453
|
+
size_bytes: packageData.byteLength,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
catch (error) {
|
|
457
|
+
return toolError("cuemap_project_pack", error);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
server.registerTool("cuemap_project_package_load", {
|
|
461
|
+
description: "Install and warm a local .cuemap package. Use only after the user explicitly approves the exact package path; existing projects are never overwritten.",
|
|
462
|
+
inputSchema: zod_1.z.object({
|
|
463
|
+
package_path: zod_1.z.string().min(1).describe("Absolute local path to the .cuemap package."),
|
|
464
|
+
confirmed: zod_1.z.boolean().optional().describe("Must be true after explicit user approval of the package path."),
|
|
465
|
+
}),
|
|
466
|
+
}, async (args) => {
|
|
467
|
+
if (!args.confirmed)
|
|
468
|
+
return confirmationRequired("project package loading");
|
|
469
|
+
try {
|
|
470
|
+
if (!path_1.default.isAbsolute(args.package_path)) {
|
|
471
|
+
throw new Error("package_path must be absolute");
|
|
472
|
+
}
|
|
473
|
+
const stats = (0, node_fs_1.statSync)(args.package_path);
|
|
474
|
+
if (!stats.isFile())
|
|
475
|
+
throw new Error("package_path must reference a regular file");
|
|
476
|
+
const response = await engineRawRequest("POST", "/projects/load", (0, node_fs_1.readFileSync)(args.package_path), undefined, "application/vnd.cuemap.project");
|
|
477
|
+
return jsonToolResult(await response.json());
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
return toolError("cuemap_project_package_load", error);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
server.registerTool("cuemap_project_push", {
|
|
484
|
+
description: "Pack and upload a CueMap project with the engine host's configured AWS CLI. Use only after explicit approval of the exact S3 destination; an existing object at that URI may be replaced.",
|
|
485
|
+
inputSchema: zod_1.z.object({
|
|
486
|
+
project: zod_1.z.string().min(1).optional().describe("Project ID to push. Defaults to the repository-scoped project."),
|
|
487
|
+
destination: zod_1.z.string().startsWith("s3://").describe("Exact S3 object URI or prefix."),
|
|
488
|
+
confirmed: zod_1.z.boolean().optional().describe("Must be true after explicit user approval of the S3 destination."),
|
|
489
|
+
}),
|
|
490
|
+
}, async (args) => {
|
|
491
|
+
if (!args.confirmed)
|
|
492
|
+
return confirmationRequired("project package upload");
|
|
493
|
+
try {
|
|
494
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
495
|
+
return jsonToolResult(await engineRequest("POST", `/projects/${encodeURIComponent(project)}/push`, { destination: args.destination }, project));
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
return toolError("cuemap_project_push", error);
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
server.registerTool("cuemap_project_pull", {
|
|
502
|
+
description: "Download, install, and warm a .cuemap package with the engine host's configured AWS CLI. Use only after explicit approval of the exact S3 source; existing projects are never overwritten.",
|
|
503
|
+
inputSchema: zod_1.z.object({
|
|
504
|
+
source: zod_1.z.string().startsWith("s3://").describe("Exact S3 object URI for a .cuemap package."),
|
|
505
|
+
confirmed: zod_1.z.boolean().optional().describe("Must be true after explicit user approval of the S3 source."),
|
|
506
|
+
}),
|
|
507
|
+
}, async (args) => {
|
|
508
|
+
if (!args.confirmed)
|
|
509
|
+
return confirmationRequired("project package download and load");
|
|
510
|
+
try {
|
|
511
|
+
return jsonToolResult(await engineRequest("POST", "/projects/pull", { source: args.source }));
|
|
512
|
+
}
|
|
513
|
+
catch (error) {
|
|
514
|
+
return toolError("cuemap_project_pull", error);
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
server.registerTool("cuemap_project_sync", {
|
|
518
|
+
description: "Fast-forward a project through immutable history at an S3 sync root. Pushes local-only changes, pulls remote-only changes, and refuses divergent histories or stale concurrent writes. Use only after explicit approval of the project and exact S3 root.",
|
|
519
|
+
inputSchema: zod_1.z.object({
|
|
520
|
+
project: zod_1.z.string().min(1).optional().describe("Project ID to synchronize. Defaults to the repository-scoped project."),
|
|
521
|
+
remote: zod_1.z.string().startsWith("s3://").describe("Exact S3 root used for this project's sync history."),
|
|
522
|
+
confirmed: zod_1.z.boolean().optional().describe("Must be true after explicit user approval of the project and S3 sync root."),
|
|
523
|
+
}),
|
|
524
|
+
}, async (args) => {
|
|
525
|
+
if (!args.confirmed)
|
|
526
|
+
return confirmationRequired("project synchronization");
|
|
527
|
+
try {
|
|
528
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
529
|
+
return jsonToolResult(await engineRequest("POST", `/projects/${encodeURIComponent(project)}/sync`, { remote: args.remote }, project));
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
return toolError("cuemap_project_sync", error);
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
server.registerTool("cuemap_stats", {
|
|
536
|
+
description: "Read CueMap statistics for the repository-scoped project or globally across the engine.",
|
|
537
|
+
inputSchema: zod_1.z.object({
|
|
538
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
539
|
+
global: zod_1.z.boolean().optional().describe("Return global engine statistics instead of project statistics. Default is false."),
|
|
540
|
+
}),
|
|
541
|
+
}, async (args) => {
|
|
542
|
+
try {
|
|
543
|
+
if (args.global && args.project) {
|
|
544
|
+
throw new Error("project and global cannot be supplied together");
|
|
545
|
+
}
|
|
546
|
+
const project = args.global ? undefined : (args.project || DEFAULT_PROJECT);
|
|
547
|
+
return jsonToolResult(await engineRequest("GET", "/stats", undefined, project));
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
return toolError("cuemap_stats", error);
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
server.registerTool("cuemap_memory_get", {
|
|
554
|
+
description: "Get one stored memory as readable text with source metadata. Pass the memory_id and owning project_id from recall as memory_id and project. Does not expand neighbors or read live source files.",
|
|
555
|
+
inputSchema: zod_1.z.object({
|
|
556
|
+
memory_id: zod_1.z.number().int().nonnegative().max(4_294_967_295),
|
|
557
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
558
|
+
}),
|
|
559
|
+
}, async (args) => {
|
|
560
|
+
try {
|
|
561
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
562
|
+
return (0, recall_result_js_1.memoryToolResult)(await engineRequest("GET", `/memories/${args.memory_id}?decoded=true`, undefined, project));
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
return toolError("cuemap_memory_get", error);
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
server.registerTool("cuemap_memory_reinforce", {
|
|
569
|
+
description: "Reinforce one CueMap memory, optionally along specific cue pathways.",
|
|
570
|
+
inputSchema: zod_1.z.object({
|
|
571
|
+
memory_id: zod_1.z.number().int().nonnegative().max(4_294_967_295),
|
|
572
|
+
cues: zod_1.z.array(zod_1.z.string().min(1)).optional(),
|
|
573
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
574
|
+
}),
|
|
575
|
+
}, async (args) => {
|
|
576
|
+
try {
|
|
577
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
578
|
+
return jsonToolResult(await engineRequest("PATCH", `/memories/${args.memory_id}/reinforce`, { cues: args.cues || [] }, project));
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
return toolError("cuemap_memory_reinforce", error);
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
server.registerTool("cuemap_memory_delete", {
|
|
585
|
+
description: "Permanently delete one CueMap memory. Set confirmed=true only after explicit user confirmation.",
|
|
586
|
+
inputSchema: zod_1.z.object({
|
|
587
|
+
memory_id: zod_1.z.number().int().nonnegative().max(4_294_967_295),
|
|
588
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
589
|
+
confirmed: zod_1.z.boolean().describe("Must be true only after the user explicitly confirms permanent deletion."),
|
|
590
|
+
}),
|
|
591
|
+
}, async (args) => {
|
|
592
|
+
if (!args.confirmed)
|
|
593
|
+
return confirmationRequired("memory deletion");
|
|
594
|
+
try {
|
|
595
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
596
|
+
return jsonToolResult(await engineRequest("DELETE", `/memories/${args.memory_id}`, undefined, project));
|
|
597
|
+
}
|
|
598
|
+
catch (error) {
|
|
599
|
+
return toolError("cuemap_memory_delete", error);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
server.registerTool("cuemap_ingest_url", {
|
|
603
|
+
description: "Explicitly ingest content from a URL, optionally crawling same-domain links. Use only when the user asks to ingest that URL.",
|
|
604
|
+
inputSchema: zod_1.z.object({
|
|
605
|
+
url: zod_1.z.string().url(),
|
|
606
|
+
depth: zod_1.z.number().int().nonnegative().max(10).optional().describe("Crawl depth. Zero ingests only the supplied page."),
|
|
607
|
+
same_domain_only: zod_1.z.boolean().optional().describe("Restrict recursive crawling to the starting domain. Default is true."),
|
|
608
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
609
|
+
}),
|
|
610
|
+
}, async (args) => {
|
|
611
|
+
try {
|
|
612
|
+
const result = await projectClient(args.project).ingestUrl(args.url, args.depth || 0, args.same_domain_only ?? true);
|
|
613
|
+
return jsonToolResult(result);
|
|
614
|
+
}
|
|
615
|
+
catch (error) {
|
|
616
|
+
return toolError("cuemap_ingest_url", error);
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
server.registerTool("cuemap_ingest_content", {
|
|
620
|
+
description: "Explicitly ingest supplied raw content into CueMap. Use only when the user asks to persist that content.",
|
|
621
|
+
inputSchema: zod_1.z.object({
|
|
622
|
+
content: zod_1.z.string().min(1),
|
|
623
|
+
filename: zod_1.z.string().min(1).optional().describe("Logical source filename used for type detection. Default is content.txt."),
|
|
624
|
+
source_key: zod_1.z.string().optional().describe("Stable source key for deterministic replacement or deduplication."),
|
|
625
|
+
metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
626
|
+
structural_cues: zod_1.z.array(zod_1.z.string()).optional().describe("Normally omit: CueMap extracts structural cues automatically. Optionally add reliable source structure or a reusable category such as type:conversation."),
|
|
627
|
+
embeddings: zod_1.z.array(zod_1.z.array(zod_1.z.number()).nonempty()).optional().describe("Optional one-vector-per-produced-chunk embeddings."),
|
|
628
|
+
segmenter: zod_1.z.enum(["sentence_window", "logical_block"]).optional(),
|
|
629
|
+
segment_window_size: zod_1.z.number().int().positive().optional(),
|
|
630
|
+
segment_overlap: zod_1.z.number().int().nonnegative().optional(),
|
|
631
|
+
segment_min_chunk_chars: zod_1.z.number().int().positive().optional(),
|
|
632
|
+
segment_max_chunk_chars: zod_1.z.number().int().positive().optional(),
|
|
633
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
634
|
+
}),
|
|
635
|
+
}, async (args) => {
|
|
636
|
+
try {
|
|
637
|
+
const result = await projectClient(args.project).ingestContent(args.content, args.filename || "content.txt", {
|
|
638
|
+
sourceKey: args.source_key,
|
|
639
|
+
metadata: args.metadata,
|
|
640
|
+
structuralCues: args.structural_cues,
|
|
641
|
+
embeddings: args.embeddings,
|
|
642
|
+
segmenter: args.segmenter,
|
|
643
|
+
segmentWindowSize: args.segment_window_size,
|
|
644
|
+
segmentOverlap: args.segment_overlap,
|
|
645
|
+
segmentMinChunkChars: args.segment_min_chunk_chars,
|
|
646
|
+
segmentMaxChunkChars: args.segment_max_chunk_chars,
|
|
647
|
+
});
|
|
648
|
+
return jsonToolResult(result);
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
return toolError("cuemap_ingest_content", error);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
server.registerTool("cuemap_ingest_file", {
|
|
655
|
+
description: "Explicitly ingest one local file into CueMap. Use only for a file the user has placed in scope and asked to ingest.",
|
|
656
|
+
inputSchema: zod_1.z.object({
|
|
657
|
+
path: zod_1.z.string().min(1).describe("Absolute or repository-relative path to the file."),
|
|
658
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
659
|
+
}),
|
|
660
|
+
}, async (args) => {
|
|
661
|
+
try {
|
|
662
|
+
const filePath = path_1.default.resolve(args.path);
|
|
663
|
+
if (!(0, node_fs_1.statSync)(filePath).isFile()) {
|
|
664
|
+
throw new Error(`Not a regular file: ${filePath}`);
|
|
665
|
+
}
|
|
666
|
+
const file = new node_buffer_1.File([(0, node_fs_1.readFileSync)(filePath)], path_1.default.basename(filePath), { type: "application/octet-stream" });
|
|
667
|
+
return jsonToolResult(await projectClient(args.project).ingestFile(file));
|
|
668
|
+
}
|
|
669
|
+
catch (error) {
|
|
670
|
+
return toolError("cuemap_ingest_file", error);
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
server.registerTool("cuemap_project_export", {
|
|
674
|
+
description: "Export a cursor-paginated page of memories from a CueMap project.",
|
|
675
|
+
inputSchema: zod_1.z.object({
|
|
676
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
677
|
+
cursor: zod_1.z.union([zod_1.z.number().int().nonnegative(), zod_1.z.string().min(1)]).optional(),
|
|
678
|
+
limit: zod_1.z.number().int().positive().max(10_000).optional(),
|
|
679
|
+
include_content: zod_1.z.boolean().optional(),
|
|
680
|
+
include_cues: zod_1.z.boolean().optional(),
|
|
681
|
+
include_metadata: zod_1.z.boolean().optional(),
|
|
682
|
+
}),
|
|
683
|
+
}, async (args) => {
|
|
684
|
+
try {
|
|
685
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
686
|
+
return jsonToolResult(await projectClient(project).exportProject(project, {
|
|
687
|
+
cursor: args.cursor,
|
|
688
|
+
limit: args.limit,
|
|
689
|
+
includeContent: args.include_content,
|
|
690
|
+
includeCues: args.include_cues,
|
|
691
|
+
includeMetadata: args.include_metadata,
|
|
692
|
+
}));
|
|
693
|
+
}
|
|
694
|
+
catch (error) {
|
|
695
|
+
return toolError("cuemap_project_export", error);
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
server.registerTool("cuemap_project_artifacts", {
|
|
699
|
+
description: "Inspect CueBridge artifact metadata for a CueMap project without reloading or mutating it.",
|
|
700
|
+
inputSchema: zod_1.z.object({
|
|
701
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
702
|
+
}),
|
|
703
|
+
}, async (args) => {
|
|
704
|
+
try {
|
|
705
|
+
const project = args.project || DEFAULT_PROJECT;
|
|
706
|
+
return jsonToolResult(await projectClient(project).projectArtifacts(project));
|
|
707
|
+
}
|
|
708
|
+
catch (error) {
|
|
709
|
+
return toolError("cuemap_project_artifacts", error);
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
server.registerTool("cuemap_alias_list", {
|
|
713
|
+
description: "List manual cue aliases associated with one cue.",
|
|
714
|
+
inputSchema: zod_1.z.object({
|
|
715
|
+
cue: zod_1.z.string().min(1),
|
|
716
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
717
|
+
}),
|
|
718
|
+
}, async (args) => {
|
|
719
|
+
try {
|
|
720
|
+
return jsonToolResult(await projectClient(args.project).getAliases(args.cue));
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
return toolError("cuemap_alias_list", error);
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
server.registerTool("cuemap_alias_add", {
|
|
727
|
+
description: "Add a manual weighted mapping from one cue to another.",
|
|
728
|
+
inputSchema: zod_1.z.object({
|
|
729
|
+
from: zod_1.z.string().min(1),
|
|
730
|
+
to: zod_1.z.string().min(1),
|
|
731
|
+
weight: zod_1.z.number().min(0).max(1).optional().describe("Association weight from 0 to 1. Default is 1."),
|
|
732
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
733
|
+
}),
|
|
734
|
+
}, async (args) => {
|
|
735
|
+
try {
|
|
736
|
+
const added = await projectClient(args.project).addAlias(args.from, args.to, args.weight ?? 1);
|
|
737
|
+
if (!added)
|
|
738
|
+
throw new Error("CueMap rejected the alias mapping");
|
|
739
|
+
return jsonToolResult({ added: true, from: args.from, to: args.to, weight: args.weight ?? 1 });
|
|
740
|
+
}
|
|
741
|
+
catch (error) {
|
|
742
|
+
return toolError("cuemap_alias_add", error);
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
server.registerTool("cuemap_alias_merge", {
|
|
746
|
+
description: "Merge multiple cues into one canonical cue. Set confirmed=true only after explicit user confirmation.",
|
|
747
|
+
inputSchema: zod_1.z.object({
|
|
748
|
+
cues: zod_1.z.array(zod_1.z.string().min(1)).min(2),
|
|
749
|
+
to: zod_1.z.string().min(1),
|
|
750
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
751
|
+
confirmed: zod_1.z.boolean().describe("Must be true only after the user explicitly confirms the merge."),
|
|
752
|
+
}),
|
|
753
|
+
}, async (args) => {
|
|
754
|
+
if (!args.confirmed)
|
|
755
|
+
return confirmationRequired("alias merging");
|
|
756
|
+
try {
|
|
757
|
+
const merged = await projectClient(args.project).mergeAliases(args.cues, args.to);
|
|
758
|
+
if (!merged)
|
|
759
|
+
throw new Error("CueMap rejected the alias merge");
|
|
760
|
+
return jsonToolResult({ merged: true, cues: args.cues, to: args.to });
|
|
761
|
+
}
|
|
762
|
+
catch (error) {
|
|
763
|
+
return toolError("cuemap_alias_merge", error);
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
server.registerTool("cuemap_lexicon_inspect", {
|
|
767
|
+
description: "Inspect one cue and its Lexicon relationships.",
|
|
768
|
+
inputSchema: zod_1.z.object({
|
|
769
|
+
cue: zod_1.z.string().min(1),
|
|
770
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
771
|
+
}),
|
|
772
|
+
}, async (args) => {
|
|
773
|
+
try {
|
|
774
|
+
return jsonToolResult(await projectClient(args.project).lexiconInspect(args.cue));
|
|
775
|
+
}
|
|
776
|
+
catch (error) {
|
|
777
|
+
return toolError("cuemap_lexicon_inspect", error);
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
server.registerTool("cuemap_lexicon_graph", {
|
|
781
|
+
description: "Read the current Lexicon graph for a project.",
|
|
782
|
+
inputSchema: zod_1.z.object({
|
|
783
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
784
|
+
}),
|
|
785
|
+
}, async (args) => {
|
|
786
|
+
try {
|
|
787
|
+
return jsonToolResult(await projectClient(args.project).lexiconGraph());
|
|
788
|
+
}
|
|
789
|
+
catch (error) {
|
|
790
|
+
return toolError("cuemap_lexicon_graph", error);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
server.registerTool("cuemap_lexicon_wire", {
|
|
794
|
+
description: "Manually wire a token to a canonical Lexicon cue.",
|
|
795
|
+
inputSchema: zod_1.z.object({
|
|
796
|
+
token: zod_1.z.string().min(1),
|
|
797
|
+
canonical: zod_1.z.string().min(1),
|
|
798
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
799
|
+
}),
|
|
800
|
+
}, async (args) => {
|
|
801
|
+
try {
|
|
802
|
+
return jsonToolResult(await projectClient(args.project).lexiconWire(args.token, args.canonical));
|
|
803
|
+
}
|
|
804
|
+
catch (error) {
|
|
805
|
+
return toolError("cuemap_lexicon_wire", error);
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
server.registerTool("cuemap_lexicon_delete", {
|
|
809
|
+
description: "Permanently delete one Lexicon entry. Set confirmed=true only after explicit user confirmation.",
|
|
810
|
+
inputSchema: zod_1.z.object({
|
|
811
|
+
entry_id: zod_1.z.union([zod_1.z.number().int().nonnegative(), zod_1.z.string().min(1)]),
|
|
812
|
+
project: zod_1.z.string().min(1).optional().describe("Optional project ID. Defaults to the repository-scoped project."),
|
|
813
|
+
confirmed: zod_1.z.boolean().describe("Must be true only after the user explicitly confirms permanent deletion."),
|
|
814
|
+
}),
|
|
815
|
+
}, async (args) => {
|
|
816
|
+
if (!args.confirmed)
|
|
817
|
+
return confirmationRequired("Lexicon entry deletion");
|
|
818
|
+
try {
|
|
819
|
+
const deleted = await projectClient(args.project).lexiconDelete(String(args.entry_id));
|
|
820
|
+
if (!deleted)
|
|
821
|
+
throw new Error("CueMap did not delete the Lexicon entry");
|
|
822
|
+
return jsonToolResult({ deleted: true, entry_id: args.entry_id });
|
|
823
|
+
}
|
|
824
|
+
catch (error) {
|
|
825
|
+
return toolError("cuemap_lexicon_delete", error);
|
|
826
|
+
}
|
|
827
|
+
});
|
|
179
828
|
server.registerTool("cuemap_recall", {
|
|
180
|
-
description: "Recall
|
|
829
|
+
description: "Recall evidence for a focused question; follow up with narrower queries as needed. Returns engine JSON with project_id and memory_id handles, source metadata, and requested diagnostics in text and structuredContent. Use handles with cuemap_memory_get when the stored record is needed. Hybrid locally reranks lexical candidates. Start with a small limit and depth 1; enable reconstruction only when surrounding evidence is needed.",
|
|
181
830
|
inputSchema: zod_1.z.object({
|
|
182
831
|
query: zod_1.z.string().describe("The natural language query to search the codebase memory for."),
|
|
832
|
+
response_mode: zod_1.z.enum(["full", "preview"]).optional().describe("Default full. Use preview for broad discovery to return only a leading excerpt per hit, with IDs and source metadata. Fetch promising stored memories with cuemap_memory_get; previews are not complete evidence."),
|
|
833
|
+
preview_chars: zod_1.z.number().int().min(100).max(2000).optional().describe("Maximum leading content length per hit in preview mode (100–2000 UTF-16 code units; default 200). Does not cap metadata or diagnostics. Ignored in full mode."),
|
|
183
834
|
limit: zod_1.z.number().optional().describe("Optional limit on the number of results to return. Default is 10."),
|
|
184
835
|
projects: zod_1.z.array(zod_1.z.string()).optional().describe("Optional list of project IDs to scope the search to. Provide multiple for cross-project recall. If not provided, searches the default project."),
|
|
185
|
-
cues: zod_1.z.array(zod_1.z.string()).optional().describe("
|
|
836
|
+
cues: zod_1.z.array(zod_1.z.string()).optional().describe("Normally omit: CueMap generates cues from the query. Supply known tags only to deliberately narrow lexical/hybrid recall, e.g. type:conversation to search tagged conversations."),
|
|
186
837
|
query_time: zod_1.z.string().optional().describe("Optional timestamp or natural-language time anchor used by v0.7 temporal query intent."),
|
|
187
838
|
depth: zod_1.z.number().optional().describe("Depth of multi-hop recall. Default is 1."),
|
|
188
|
-
expansion_depth: zod_1.z.number().optional().describe("
|
|
839
|
+
expansion_depth: zod_1.z.number().optional().describe("Neighbor context expansion. 1 returns the matched chunk; values above 1 include nearby parent chunks or source-ordered context with radius expansion_depth - 1 when linkage exists. Default is 1."),
|
|
189
840
|
auto_reinforce: zod_1.z.boolean().optional().describe("Automatically reinforce retrieved memories. Default is false."),
|
|
190
841
|
min_intersection: zod_1.z.number().optional().describe("Minimum intersection count for retrieval. Default is 0."),
|
|
191
842
|
explain: zod_1.z.boolean().optional().describe("Include explain component for debug information in results. Default is false."),
|
|
192
843
|
trace_timing: zod_1.z.boolean().optional().describe("Include v0.7 timing diagnostics in the response. Default is false."),
|
|
193
844
|
disable_salience_bias: zod_1.z.boolean().optional().describe("Disable salience bias scoring. Default is false."),
|
|
194
845
|
disable_alias_expansion: zod_1.z.boolean().optional().describe("Disable alias expansion during querying. Default is true."),
|
|
195
|
-
cuepacks: zod_1.z.array(zod_1.z.string()).optional().describe("Optional cuepack names to apply during query-intent expansion."),
|
|
196
846
|
parent_fusion: zod_1.z.enum(["off", "auto", "force"]).optional().describe("Parent fusion mode for chunk-parent reconstruction. Default is off."),
|
|
197
847
|
parent_fusion_limit: zod_1.z.number().optional().describe("Candidate limit for parent fusion. Default is 80."),
|
|
198
848
|
parent_fusion_min_chunks: zod_1.z.number().optional().describe("Minimum sibling chunks required for parent fusion. Default is 2."),
|
|
@@ -206,12 +856,16 @@ async function main() {
|
|
|
206
856
|
evidence_coverage_max_sessions: zod_1.z.number().optional().describe("Maximum sessions considered for evidence coverage. Default is 3."),
|
|
207
857
|
disable_cuebridge_artifacts: zod_1.z.boolean().optional().describe("Disable CueBridge artifact expansion. Default is false."),
|
|
208
858
|
cuebridge_gap_limit: zod_1.z.number().optional().describe("Maximum CueBridge gap expansions. Default is 6."),
|
|
859
|
+
semantic_mode: zod_1.z.enum(["lexical", "semantic", "hybrid"]).optional().describe("Query signal mode. lexical uses cue recall only, semantic uses vector candidate discovery, and hybrid reranks lexical candidates. Default is hybrid."),
|
|
860
|
+
query_embedding: zod_1.z.array(zod_1.z.number()).optional().describe("Optional precomputed query vector. Use this when the application owns the embedding provider."),
|
|
209
861
|
}),
|
|
210
862
|
}, async (args) => {
|
|
211
863
|
try {
|
|
212
|
-
const { query, limit = 10, projects, cues, query_time, depth = 1, expansion_depth = 1, auto_reinforce = false, min_intersection, explain = false, trace_timing = false, disable_salience_bias = false, disable_alias_expansion = true,
|
|
864
|
+
const { query, limit = 10, projects, cues, query_time, response_mode = "full", preview_chars = 200, depth = 1, expansion_depth = 1, auto_reinforce = false, min_intersection, explain = false, trace_timing = false, disable_salience_bias = false, disable_alias_expansion = true, parent_fusion = "off", parent_fusion_limit = 80, parent_fusion_min_chunks = 2, ordered_reconstruction = "off", ordered_reconstruction_limit = 80, ordered_session_scan_limit = 4096, ordered_max_sessions = 3, evidence_coverage = "off", evidence_coverage_limit = 100, evidence_coverage_session_scan_limit = 4096, evidence_coverage_max_sessions = 3, disable_cuebridge_artifacts = false, cuebridge_gap_limit = 6, semantic_mode = "hybrid", query_embedding } = args;
|
|
213
865
|
const results = await client.recall({
|
|
214
866
|
query_text: query,
|
|
867
|
+
response_mode,
|
|
868
|
+
preview_chars,
|
|
215
869
|
cues,
|
|
216
870
|
projects,
|
|
217
871
|
limit,
|
|
@@ -224,7 +878,6 @@ async function main() {
|
|
|
224
878
|
trace_timing,
|
|
225
879
|
disable_salience_bias,
|
|
226
880
|
disable_alias_expansion,
|
|
227
|
-
cuepacks,
|
|
228
881
|
parent_fusion,
|
|
229
882
|
parent_fusion_limit,
|
|
230
883
|
parent_fusion_min_chunks,
|
|
@@ -238,55 +891,10 @@ async function main() {
|
|
|
238
891
|
evidence_coverage_max_sessions,
|
|
239
892
|
disable_cuebridge_artifacts,
|
|
240
893
|
cuebridge_gap_limit,
|
|
894
|
+
semantic_mode,
|
|
895
|
+
query_embedding,
|
|
241
896
|
});
|
|
242
|
-
|
|
243
|
-
if (results.results && Array.isArray(results.results)) {
|
|
244
|
-
if (results.results.length > 0 && results.results[0].project_id) {
|
|
245
|
-
results.results.forEach((projectRes) => {
|
|
246
|
-
if (projectRes.results && Array.isArray(projectRes.results)) {
|
|
247
|
-
items = items.concat(projectRes.results.map((r) => ({ ...r, project_id: projectRes.project_id })));
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
items = results.results;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
if (!items || items.length === 0) {
|
|
256
|
-
return {
|
|
257
|
-
content: [
|
|
258
|
-
{
|
|
259
|
-
type: "text",
|
|
260
|
-
text: "No results found for the query in CueMap.",
|
|
261
|
-
},
|
|
262
|
-
],
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
let formattedText = `CueMap found ${items.length} relevant results:\n\n`;
|
|
266
|
-
items.forEach((r, i) => {
|
|
267
|
-
const scoreStr = r.score !== undefined ? ` (Score: ${Number(r.score).toFixed(2)})` : '';
|
|
268
|
-
formattedText += `### Result ${i + 1}${scoreStr}\n`;
|
|
269
|
-
const projectId = r.project_id || (projects && projects.length === 1 ? projects[0] : null);
|
|
270
|
-
if (projectId)
|
|
271
|
-
formattedText += `*Project: ${projectId}*\n`;
|
|
272
|
-
if (r.timestamp) {
|
|
273
|
-
const date = new Date(r.timestamp);
|
|
274
|
-
formattedText += `*Timestamp: ${date.toISOString()}*\n`;
|
|
275
|
-
}
|
|
276
|
-
else if (r.created_at) {
|
|
277
|
-
const date = new Date(r.created_at * 1000);
|
|
278
|
-
formattedText += `*Timestamp: ${date.toISOString()}*\n`;
|
|
279
|
-
}
|
|
280
|
-
formattedText += `${r.content}\n\n`;
|
|
281
|
-
});
|
|
282
|
-
return {
|
|
283
|
-
content: [
|
|
284
|
-
{
|
|
285
|
-
type: "text",
|
|
286
|
-
text: formattedText,
|
|
287
|
-
},
|
|
288
|
-
],
|
|
289
|
-
};
|
|
897
|
+
return (0, recall_result_js_1.recallToolResult)(results, !projects?.length ? DEFAULT_PROJECT : projects.length === 1 ? projects[0] : undefined, { response_mode });
|
|
290
898
|
}
|
|
291
899
|
catch (error) {
|
|
292
900
|
console.error("Error calling CueMap engine", error);
|
|
@@ -303,7 +911,7 @@ async function main() {
|
|
|
303
911
|
});
|
|
304
912
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
305
913
|
await server.connect(transport);
|
|
306
|
-
console.error(
|
|
914
|
+
console.error(`CueMap MCP server running on stdio (default project: ${DEFAULT_PROJECT})`);
|
|
307
915
|
}
|
|
308
916
|
main().catch((error) => {
|
|
309
917
|
console.error("Server error:", error);
|