@kage-core/kage-graph-mcp 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.listTools = listTools;
5
+ exports.callTool = callTool;
4
6
  const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
5
7
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
8
  const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
9
+ const kernel_js_1 = require("./kernel.js");
7
10
  const BASE_URL = "https://raw.githubusercontent.com/kage-core/kage-graph/master";
8
11
  async function fetchText(url) {
9
12
  const res = await fetch(url);
@@ -15,12 +18,18 @@ async function fetchJSON(url) {
15
18
  const text = await fetchText(url);
16
19
  return JSON.parse(text);
17
20
  }
21
+ function domainNodeCount(domain) {
22
+ return (0, kernel_js_1.catalogDomainNodeCount)(domain);
23
+ }
24
+ function domainTopTags(domain) {
25
+ return domain.top_tags ?? [];
26
+ }
18
27
  function scoreMatch(query, node) {
19
- const terms = query.toLowerCase().split(/\s+/);
28
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
20
29
  let score = 0;
21
30
  const title = node.title.toLowerCase();
22
31
  const summary = (node.summary || "").toLowerCase();
23
- const tags = node.tags.map((t) => t.toLowerCase());
32
+ const tags = (node.tags ?? []).map((t) => t.toLowerCase());
24
33
  for (const term of terms) {
25
34
  if (title.includes(term))
26
35
  score += 3;
@@ -32,14 +41,22 @@ function scoreMatch(query, node) {
32
41
  return score;
33
42
  }
34
43
  function scoreDomainMatch(query, domain) {
35
- const terms = query.toLowerCase().split(/\s+/);
44
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
45
+ const tags = domainTopTags(domain);
36
46
  return terms.reduce((sum, term) => {
37
- return sum + domain.top_tags.filter((t) => t.includes(term)).length;
47
+ return sum + tags.filter((t) => t.includes(term)).length;
38
48
  }, 0);
39
49
  }
40
- const server = new index_js_1.Server({ name: "kage-graph", version: "1.0.0" }, { capabilities: { tools: {} } });
41
- server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
42
- tools: [
50
+ function arrayArg(value) {
51
+ if (Array.isArray(value))
52
+ return value.map(String);
53
+ if (typeof value === "string")
54
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
55
+ return [];
56
+ }
57
+ const server = new index_js_1.Server({ name: "kage-graph", version: "1.1.0" }, { capabilities: { tools: {} } });
58
+ function listTools() {
59
+ return [
43
60
  {
44
61
  name: "kage_search",
45
62
  description: "Search the kage community knowledge graph for gotchas, patterns, configs, and architectural decisions across auth, database, payments, deployment, frontend, testing, and more. Returns node summaries ranked by relevance.",
@@ -84,16 +101,381 @@ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
84
101
  properties: {},
85
102
  },
86
103
  },
87
- ],
104
+ {
105
+ name: "kage_recall",
106
+ description: "Recall repo-local Kage memory from .agent_memory packets. Returns an agent-ready context block plus ranked packet summaries.",
107
+ inputSchema: {
108
+ type: "object",
109
+ properties: {
110
+ query: { type: "string" },
111
+ project_dir: { type: "string" },
112
+ limit: { type: "number" },
113
+ explain: { type: "boolean" },
114
+ json: { type: "boolean" },
115
+ },
116
+ required: ["query", "project_dir"],
117
+ },
118
+ },
119
+ {
120
+ name: "kage_graph",
121
+ description: "Query the repo-local Kage knowledge graph. Returns typed, evidence-backed graph facts from entities, edges, and episodes.",
122
+ inputSchema: {
123
+ type: "object",
124
+ properties: {
125
+ project_dir: { type: "string" },
126
+ query: { type: "string" },
127
+ limit: { type: "number" },
128
+ },
129
+ required: ["project_dir", "query"],
130
+ },
131
+ },
132
+ {
133
+ name: "kage_code_graph",
134
+ description: "Query the source-derived codebase graph: files, symbols, imports, calls, routes, tests, package scripts. This is generated from code, not learned memory.",
135
+ inputSchema: {
136
+ type: "object",
137
+ properties: {
138
+ project_dir: { type: "string" },
139
+ query: { type: "string" },
140
+ limit: { type: "number" },
141
+ json: { type: "boolean" },
142
+ },
143
+ required: ["project_dir"],
144
+ },
145
+ },
146
+ {
147
+ name: "kage_metrics",
148
+ description: "Return concise Kage adoption and quality metrics: code graph counts, language/parser coverage, memory graph evidence coverage, pending/approved packets, validation state, and readiness score.",
149
+ inputSchema: {
150
+ type: "object",
151
+ properties: {
152
+ project_dir: { type: "string" },
153
+ },
154
+ required: ["project_dir"],
155
+ },
156
+ },
157
+ {
158
+ name: "kage_quality",
159
+ description: "Return memory quality metrics: useful memory ratio, duplicate burden, stale/wrong feedback, evidence coverage, path grounding, and review queue size.",
160
+ inputSchema: {
161
+ type: "object",
162
+ properties: {
163
+ project_dir: { type: "string" },
164
+ },
165
+ required: ["project_dir"],
166
+ },
167
+ },
168
+ {
169
+ name: "kage_benchmark",
170
+ description: "Return Kage proof metrics: runbook, bug-fix, decision and code-flow coverage, recall hit rate, estimated rediscovery avoided, tokens saved, and time-to-first-use.",
171
+ inputSchema: {
172
+ type: "object",
173
+ properties: {
174
+ project_dir: { type: "string" },
175
+ },
176
+ required: ["project_dir"],
177
+ },
178
+ },
179
+ {
180
+ name: "kage_setup_agent",
181
+ description: "Generate MCP/setup instructions for Codex, Claude Code, Cursor, Windsurf, Gemini CLI, OpenCode, Cline, Goose, Roo Code, Kilo Code, Claude Desktop, Aider, or generic MCP.",
182
+ inputSchema: {
183
+ type: "object",
184
+ properties: {
185
+ agent: { type: "string", enum: kernel_js_1.SETUP_AGENTS },
186
+ project_dir: { type: "string" },
187
+ write: { type: "boolean" },
188
+ },
189
+ required: ["agent", "project_dir"],
190
+ },
191
+ },
192
+ {
193
+ name: "kage_verify_agent",
194
+ description: "Verify that Kage is truly active for the current agent: config, repo policy, indexes, recall, code graph, and this live MCP tool reachability.",
195
+ inputSchema: {
196
+ type: "object",
197
+ properties: {
198
+ agent: { type: "string", enum: kernel_js_1.SETUP_AGENTS },
199
+ project_dir: { type: "string" },
200
+ },
201
+ required: ["agent", "project_dir"],
202
+ },
203
+ },
204
+ {
205
+ name: "kage_graph_visual",
206
+ description: "Export the repo-local Kage knowledge graph as Mermaid flowchart text for visual inspection.",
207
+ inputSchema: {
208
+ type: "object",
209
+ properties: {
210
+ project_dir: { type: "string" },
211
+ limit: { type: "number" },
212
+ },
213
+ required: ["project_dir"],
214
+ },
215
+ },
216
+ {
217
+ name: "kage_branch_overlay",
218
+ description: "Build and return branch overlay metadata: branch, head, merge-base, changed files, and pending packet IDs.",
219
+ inputSchema: {
220
+ type: "object",
221
+ properties: {
222
+ project_dir: { type: "string" },
223
+ },
224
+ required: ["project_dir"],
225
+ },
226
+ },
227
+ {
228
+ name: "kage_learn",
229
+ description: "Capture an actual reusable learning from the current session as repo-local memory. Prefer this over diff proposal when the agent knows what was learned.",
230
+ inputSchema: {
231
+ type: "object",
232
+ properties: {
233
+ project_dir: { type: "string" },
234
+ learning: { type: "string" },
235
+ title: { type: "string" },
236
+ type: { type: "string" },
237
+ evidence: { type: "string" },
238
+ verified_by: { type: "string" },
239
+ tags: { type: "array", items: { type: "string" } },
240
+ paths: { type: "array", items: { type: "string" } },
241
+ stack: { type: "array", items: { type: "string" } },
242
+ },
243
+ required: ["project_dir", "learning"],
244
+ },
245
+ },
246
+ {
247
+ name: "kage_capture",
248
+ description: "Create a repo-local Kage memory packet immediately. Org/global promotion still requires explicit human review.",
249
+ inputSchema: {
250
+ type: "object",
251
+ properties: {
252
+ project_dir: { type: "string" },
253
+ title: { type: "string" },
254
+ summary: { type: "string" },
255
+ body: { type: "string" },
256
+ type: { type: "string" },
257
+ tags: { type: "array", items: { type: "string" } },
258
+ paths: { type: "array", items: { type: "string" } },
259
+ stack: { type: "array", items: { type: "string" } },
260
+ },
261
+ required: ["project_dir", "title", "body"],
262
+ },
263
+ },
264
+ {
265
+ name: "kage_observe",
266
+ description: "Store an automatic local observation event from an agent session. Observations are privacy-scanned, deduplicated, and never published automatically.",
267
+ inputSchema: {
268
+ type: "object",
269
+ properties: {
270
+ project_dir: { type: "string" },
271
+ type: { type: "string", enum: ["session_start", "user_prompt", "tool_use", "tool_result", "file_change", "command_result", "test_result", "session_end"] },
272
+ session_id: { type: "string" },
273
+ agent: { type: "string" },
274
+ tool: { type: "string" },
275
+ path: { type: "string" },
276
+ command: { type: "string" },
277
+ exit_code: { type: "number" },
278
+ text: { type: "string" },
279
+ summary: { type: "string" },
280
+ timestamp: { type: "string" },
281
+ metadata: { type: "object" },
282
+ },
283
+ required: ["project_dir", "type"],
284
+ },
285
+ },
286
+ {
287
+ name: "kage_distill",
288
+ description: "Distill stored observations for one session into repo-local memory candidates. Org/global promotion still requires explicit human review.",
289
+ inputSchema: {
290
+ type: "object",
291
+ properties: {
292
+ project_dir: { type: "string" },
293
+ session_id: { type: "string" },
294
+ },
295
+ required: ["project_dir", "session_id"],
296
+ },
297
+ },
298
+ {
299
+ name: "kage_feedback",
300
+ description: "Record usefulness feedback on an approved repo-local memory packet: helpful, wrong, or stale.",
301
+ inputSchema: {
302
+ type: "object",
303
+ properties: {
304
+ project_dir: { type: "string" },
305
+ packet_id: { type: "string" },
306
+ kind: { type: "string", enum: ["helpful", "wrong", "stale"] },
307
+ },
308
+ required: ["project_dir", "packet_id", "kind"],
309
+ },
310
+ },
311
+ {
312
+ name: "kage_install_policy",
313
+ description: "Install or update the repo AGENTS.md policy that tells coding agents to use Kage automatically.",
314
+ inputSchema: {
315
+ type: "object",
316
+ properties: {
317
+ project_dir: { type: "string" },
318
+ },
319
+ required: ["project_dir"],
320
+ },
321
+ },
322
+ {
323
+ name: "kage_validate",
324
+ description: "Validate repo-local Kage memory packets, pending packets, generated indexes, and sensitive-content checks.",
325
+ inputSchema: {
326
+ type: "object",
327
+ properties: {
328
+ project_dir: { type: "string" },
329
+ },
330
+ required: ["project_dir"],
331
+ },
332
+ },
333
+ {
334
+ name: "kage_registry_recommend",
335
+ description: "Recommend documentation packs, skills, and optional MCPs for this repo based on its package metadata. Recommendations never install anything automatically.",
336
+ inputSchema: {
337
+ type: "object",
338
+ properties: {
339
+ project_dir: { type: "string" },
340
+ },
341
+ required: ["project_dir"],
342
+ },
343
+ },
344
+ {
345
+ name: "kage_marketplace",
346
+ description: "Build a local marketplace manifest for recommended docs, skills, and MCP packs. This never installs anything automatically.",
347
+ inputSchema: {
348
+ type: "object",
349
+ properties: {
350
+ project_dir: { type: "string" },
351
+ },
352
+ required: ["project_dir"],
353
+ },
354
+ },
355
+ {
356
+ name: "kage_org_status",
357
+ description: "Inspect the local org-memory inbox, approved packets, rejected packets, audit count, and registry path.",
358
+ inputSchema: {
359
+ type: "object",
360
+ properties: {
361
+ project_dir: { type: "string" },
362
+ org: { type: "string" },
363
+ },
364
+ required: ["project_dir", "org"],
365
+ },
366
+ },
367
+ {
368
+ name: "kage_org_upload_candidate",
369
+ description: "Upload an approved repo packet into the local org review inbox. This creates a candidate only; it does not approve org memory.",
370
+ inputSchema: {
371
+ type: "object",
372
+ properties: {
373
+ project_dir: { type: "string" },
374
+ org: { type: "string" },
375
+ packet_id: { type: "string" },
376
+ },
377
+ required: ["project_dir", "org", "packet_id"],
378
+ },
379
+ },
380
+ {
381
+ name: "kage_org_recall",
382
+ description: "Recall approved local org memory. Repo-local recall should still take priority when results conflict.",
383
+ inputSchema: {
384
+ type: "object",
385
+ properties: {
386
+ project_dir: { type: "string" },
387
+ org: { type: "string" },
388
+ query: { type: "string" },
389
+ limit: { type: "number" },
390
+ json: { type: "boolean" },
391
+ },
392
+ required: ["project_dir", "org", "query"],
393
+ },
394
+ },
395
+ {
396
+ name: "kage_layered_recall",
397
+ description: "Recall with Kage's priority order: branch > repo local > org > global. Org/global are included only when explicitly requested.",
398
+ inputSchema: {
399
+ type: "object",
400
+ properties: {
401
+ project_dir: { type: "string" },
402
+ query: { type: "string" },
403
+ org: { type: "string" },
404
+ include_global: { type: "boolean" },
405
+ json: { type: "boolean" },
406
+ },
407
+ required: ["project_dir", "query"],
408
+ },
409
+ },
410
+ {
411
+ name: "kage_global_build",
412
+ description: "Build a local static global/CDN bundle from human-promoted public candidates and the marketplace manifest. This does not upload anywhere.",
413
+ inputSchema: {
414
+ type: "object",
415
+ properties: {
416
+ project_dir: { type: "string" },
417
+ org: { type: "string" },
418
+ },
419
+ required: ["project_dir"],
420
+ },
421
+ },
422
+ {
423
+ name: "kage_promote_public_candidate",
424
+ description: "Create a sanitized local public-review candidate from an approved repo memory packet. This does not publish to the global graph.",
425
+ inputSchema: {
426
+ type: "object",
427
+ properties: {
428
+ project_dir: { type: "string" },
429
+ packet_id: { type: "string" },
430
+ },
431
+ required: ["project_dir", "packet_id"],
432
+ },
433
+ },
434
+ {
435
+ name: "kage_export_public_bundle",
436
+ description: "Export local public-review candidates as a static public bundle catalog. This still does not publish anywhere.",
437
+ inputSchema: {
438
+ type: "object",
439
+ properties: {
440
+ project_dir: { type: "string" },
441
+ },
442
+ required: ["project_dir"],
443
+ },
444
+ },
445
+ {
446
+ name: "kage_review_artifact",
447
+ description: "Create a Markdown review artifact summarizing pending memory packets for PR or human review.",
448
+ inputSchema: {
449
+ type: "object",
450
+ properties: {
451
+ project_dir: { type: "string" },
452
+ },
453
+ required: ["project_dir"],
454
+ },
455
+ },
456
+ {
457
+ name: "kage_propose_from_diff",
458
+ description: "Create or update a branch review summary and repo-local change-memory packet from local git status and diff metadata. Org/global promotion still requires explicit human review.",
459
+ inputSchema: {
460
+ type: "object",
461
+ properties: {
462
+ project_dir: { type: "string" },
463
+ },
464
+ required: ["project_dir"],
465
+ },
466
+ },
467
+ ];
468
+ }
469
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
470
+ tools: listTools(),
88
471
  }));
89
- server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
90
- const { name, arguments: args } = request.params;
472
+ async function callTool(name, args) {
91
473
  if (name === "kage_list_domains") {
92
474
  const catalog = await fetchJSON(`${BASE_URL}/catalog.json`);
93
475
  const lines = Object.entries(catalog.domains)
94
- .filter(([, d]) => d.node_count > 0)
95
- .sort(([, a], [, b]) => b.node_count - a.node_count)
96
- .map(([domain, d]) => `**${domain}** — ${d.node_count} nodes | tags: ${d.top_tags.slice(0, 5).join(", ")}`);
476
+ .filter(([, d]) => domainNodeCount(d) > 0)
477
+ .sort(([, a], [, b]) => domainNodeCount(b) - domainNodeCount(a))
478
+ .map(([domain, d]) => `**${domain}** — ${domainNodeCount(d)} nodes | tags: ${domainTopTags(d).slice(0, 5).join(", ")}`);
97
479
  return {
98
480
  content: [
99
481
  {
@@ -107,32 +489,28 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
107
489
  const query = String(args?.query ?? "");
108
490
  const domainFilter = args?.domain ? String(args.domain) : null;
109
491
  const catalog = await fetchJSON(`${BASE_URL}/catalog.json`);
110
- // Pick domains to search
111
492
  let domainsToSearch;
112
493
  if (domainFilter) {
113
494
  domainsToSearch = [domainFilter];
114
495
  }
115
496
  else {
116
497
  domainsToSearch = Object.entries(catalog.domains)
117
- .filter(([, d]) => d.node_count > 0)
498
+ .filter(([, d]) => domainNodeCount(d) > 0)
118
499
  .map(([name, d]) => ({ name, score: scoreDomainMatch(query, d) }))
119
500
  .sort((a, b) => b.score - a.score)
120
501
  .slice(0, 3)
121
502
  .filter((d) => d.score > 0)
122
503
  .map((d) => d.name);
123
- // Fall back to all non-empty domains if no tag match
124
504
  if (domainsToSearch.length === 0) {
125
505
  domainsToSearch = Object.entries(catalog.domains)
126
- .filter(([, d]) => d.node_count > 0)
506
+ .filter(([, d]) => domainNodeCount(d) > 0)
127
507
  .map(([name]) => name);
128
508
  }
129
509
  }
130
- // Fetch indexes in parallel
131
510
  const indexResults = await Promise.allSettled(domainsToSearch.map(async (domain) => {
132
511
  const index = await fetchJSON(`${BASE_URL}/domains/${domain}/index.json`);
133
512
  return { domain, nodes: index.nodes };
134
513
  }));
135
- // Score and rank all nodes
136
514
  const scored = [];
137
515
  for (const result of indexResults) {
138
516
  if (result.status === "fulfilled") {
@@ -161,7 +539,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
161
539
  return [
162
540
  `### [${i + 1}] ${n.title}`,
163
541
  `**Domain:** ${r.domain} | **Type:** ${n.type} | **Score:** ${n.score} | **Updated:** ${n.updated}`,
164
- `**Tags:** ${n.tags.join(", ")}`,
542
+ `**Tags:** ${(n.tags ?? []).join(", ")}`,
165
543
  n.summary ? `**Summary:** ${n.summary}` : "",
166
544
  `**Fetch:** domain="${r.domain}" node_id="${n.id}"`,
167
545
  ]
@@ -185,10 +563,288 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
185
563
  content: [{ type: "text", text: content }],
186
564
  };
187
565
  }
566
+ if (name === "kage_recall") {
567
+ const result = (0, kernel_js_1.recall)(String(args?.project_dir ?? ""), String(args?.query ?? ""), Number(args?.limit ?? 5), Boolean(args?.explain));
568
+ return {
569
+ content: [{ type: "text", text: args?.json || args?.explain ? JSON.stringify(result, null, 2) : result.context_block }],
570
+ };
571
+ }
572
+ if (name === "kage_graph") {
573
+ const result = (0, kernel_js_1.queryGraph)(String(args?.project_dir ?? ""), String(args?.query ?? ""), Number(args?.limit ?? 10));
574
+ return {
575
+ content: [{ type: "text", text: result.context_block }],
576
+ };
577
+ }
578
+ if (name === "kage_code_graph") {
579
+ const projectDir = String(args?.project_dir ?? "");
580
+ const query = typeof args?.query === "string" ? args.query : "";
581
+ if (query) {
582
+ const result = (0, kernel_js_1.queryCodeGraph)(projectDir, query, Number(args?.limit ?? 10));
583
+ return {
584
+ content: [{ type: "text", text: args?.json ? JSON.stringify(result, null, 2) : result.context_block }],
585
+ };
586
+ }
587
+ const result = (0, kernel_js_1.buildCodeGraph)(projectDir);
588
+ return {
589
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
590
+ };
591
+ }
592
+ if (name === "kage_metrics") {
593
+ const result = (0, kernel_js_1.kageMetrics)(String(args?.project_dir ?? ""));
594
+ return {
595
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
596
+ };
597
+ }
598
+ if (name === "kage_quality") {
599
+ const result = (0, kernel_js_1.qualityReport)(String(args?.project_dir ?? ""));
600
+ return {
601
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
602
+ };
603
+ }
604
+ if (name === "kage_benchmark") {
605
+ const result = (0, kernel_js_1.benchmarkProject)(String(args?.project_dir ?? ""));
606
+ return {
607
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
608
+ };
609
+ }
610
+ if (name === "kage_setup_agent") {
611
+ const result = (0, kernel_js_1.setupAgent)(String(args?.agent ?? ""), String(args?.project_dir ?? ""), { write: Boolean(args?.write) });
612
+ return {
613
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
614
+ };
615
+ }
616
+ if (name === "kage_verify_agent") {
617
+ const result = (0, kernel_js_1.verifyAgentActivation)(String(args?.agent ?? ""), String(args?.project_dir ?? ""), { mcpToolReachable: true });
618
+ return {
619
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
620
+ };
621
+ }
622
+ if (name === "kage_graph_visual") {
623
+ const result = (0, kernel_js_1.graphMermaid)(String(args?.project_dir ?? ""), Number(args?.limit ?? 40));
624
+ return {
625
+ content: [{ type: "text", text: `\`\`\`mermaid\n${result.mermaid}\n\`\`\`` }],
626
+ };
627
+ }
628
+ if (name === "kage_branch_overlay") {
629
+ const result = (0, kernel_js_1.buildBranchOverlay)(String(args?.project_dir ?? ""));
630
+ return {
631
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
632
+ };
633
+ }
634
+ if (name === "kage_learn") {
635
+ const result = (0, kernel_js_1.learn)({
636
+ projectDir: String(args?.project_dir ?? ""),
637
+ learning: String(args?.learning ?? ""),
638
+ title: args?.title ? String(args.title) : undefined,
639
+ type: args?.type ? String(args.type) : undefined,
640
+ evidence: args?.evidence ? String(args.evidence) : undefined,
641
+ verifiedBy: args?.verified_by ? String(args.verified_by) : undefined,
642
+ tags: arrayArg(args?.tags),
643
+ paths: arrayArg(args?.paths),
644
+ stack: arrayArg(args?.stack),
645
+ });
646
+ return {
647
+ content: [
648
+ {
649
+ type: "text",
650
+ text: result.ok
651
+ ? `Captured session learning: ${result.path}\nRepo-local memory is written immediately. Org/global promotion still requires explicit review.`
652
+ : `Learning capture blocked:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
653
+ },
654
+ ],
655
+ isError: !result.ok,
656
+ };
657
+ }
658
+ if (name === "kage_capture") {
659
+ const result = (0, kernel_js_1.capture)({
660
+ projectDir: String(args?.project_dir ?? ""),
661
+ title: String(args?.title ?? ""),
662
+ summary: args?.summary ? String(args.summary) : undefined,
663
+ body: String(args?.body ?? ""),
664
+ type: args?.type ? String(args.type) : undefined,
665
+ tags: arrayArg(args?.tags),
666
+ paths: arrayArg(args?.paths),
667
+ stack: arrayArg(args?.stack),
668
+ });
669
+ return {
670
+ content: [
671
+ {
672
+ type: "text",
673
+ text: result.ok
674
+ ? `Captured repo-local packet: ${result.path}\nOrg/global promotion still requires explicit review.`
675
+ : `Capture blocked:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
676
+ },
677
+ ],
678
+ isError: !result.ok,
679
+ };
680
+ }
681
+ if (name === "kage_observe") {
682
+ const projectDir = String(args?.project_dir ?? "");
683
+ const event = { ...args };
684
+ delete event.project_dir;
685
+ const result = (0, kernel_js_1.observe)(projectDir, event);
686
+ return {
687
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
688
+ isError: !result.ok,
689
+ };
690
+ }
691
+ if (name === "kage_distill") {
692
+ const result = (0, kernel_js_1.distillSession)(String(args?.project_dir ?? ""), String(args?.session_id ?? "default"));
693
+ return {
694
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
695
+ isError: !result.ok,
696
+ };
697
+ }
698
+ if (name === "kage_feedback") {
699
+ const result = (0, kernel_js_1.recordFeedback)(String(args?.project_dir ?? ""), String(args?.packet_id ?? ""), String(args?.kind ?? ""));
700
+ return {
701
+ content: [
702
+ {
703
+ type: "text",
704
+ text: result.ok
705
+ ? `Recorded ${args?.kind} feedback for ${args?.packet_id}`
706
+ : `Feedback failed:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
707
+ },
708
+ ],
709
+ isError: !result.ok,
710
+ };
711
+ }
712
+ if (name === "kage_install_policy") {
713
+ const result = (0, kernel_js_1.installAgentPolicy)(String(args?.project_dir ?? ""));
714
+ return {
715
+ content: [
716
+ {
717
+ type: "text",
718
+ text: `${result.created ? "Created" : result.updated ? "Updated" : "Already current"} agent policy: ${result.path}`,
719
+ },
720
+ ],
721
+ };
722
+ }
723
+ if (name === "kage_validate") {
724
+ const result = (0, kernel_js_1.validateProject)(String(args?.project_dir ?? ""));
725
+ const text = [
726
+ result.ok ? "Validation passed." : "Validation failed.",
727
+ result.errors.length ? `\nErrors:\n${result.errors.map((error) => `- ${error}`).join("\n")}` : "",
728
+ result.warnings.length ? `\nWarnings:\n${result.warnings.map((warning) => `- ${warning}`).join("\n")}` : "",
729
+ ].join("");
730
+ return {
731
+ content: [{ type: "text", text }],
732
+ isError: !result.ok,
733
+ };
734
+ }
735
+ if (name === "kage_registry_recommend") {
736
+ const result = (0, kernel_js_1.registryRecommendations)(String(args?.project_dir ?? ""));
737
+ return {
738
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
739
+ };
740
+ }
741
+ if (name === "kage_marketplace") {
742
+ const result = (0, kernel_js_1.buildMarketplace)(String(args?.project_dir ?? ""));
743
+ return {
744
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
745
+ isError: !result.ok,
746
+ };
747
+ }
748
+ if (name === "kage_org_status") {
749
+ const result = (0, kernel_js_1.orgStatus)(String(args?.project_dir ?? ""), String(args?.org ?? "local"));
750
+ return {
751
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
752
+ };
753
+ }
754
+ if (name === "kage_org_upload_candidate") {
755
+ const result = (0, kernel_js_1.orgUploadPacket)(String(args?.project_dir ?? ""), String(args?.org ?? "local"), String(args?.packet_id ?? ""));
756
+ return {
757
+ content: [
758
+ {
759
+ type: "text",
760
+ text: result.ok
761
+ ? `Created org review candidate: ${result.path}\nApprove explicitly with: kage org review --approve`
762
+ : `Org upload blocked:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
763
+ },
764
+ ],
765
+ isError: !result.ok,
766
+ };
767
+ }
768
+ if (name === "kage_org_recall") {
769
+ const result = (0, kernel_js_1.orgRecall)(String(args?.project_dir ?? ""), String(args?.org ?? "local"), String(args?.query ?? ""), Number(args?.limit ?? 5));
770
+ return {
771
+ content: [{ type: "text", text: args?.json ? JSON.stringify(result, null, 2) : result.context_block }],
772
+ };
773
+ }
774
+ if (name === "kage_layered_recall") {
775
+ const result = (0, kernel_js_1.layeredRecall)(String(args?.project_dir ?? ""), String(args?.query ?? ""), {
776
+ org: args?.org ? String(args.org) : undefined,
777
+ includeGlobal: Boolean(args?.include_global),
778
+ });
779
+ return {
780
+ content: [{ type: "text", text: args?.json ? JSON.stringify(result, null, 2) : result.context_block }],
781
+ };
782
+ }
783
+ if (name === "kage_global_build") {
784
+ const result = (0, kernel_js_1.buildGlobalCdnBundle)(String(args?.project_dir ?? ""), String(args?.org ?? "local"));
785
+ return {
786
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
787
+ isError: !result.ok,
788
+ };
789
+ }
790
+ if (name === "kage_promote_public_candidate") {
791
+ const result = (0, kernel_js_1.createPublicCandidate)(String(args?.project_dir ?? ""), String(args?.packet_id ?? ""));
792
+ return {
793
+ content: [
794
+ {
795
+ type: "text",
796
+ text: result.ok
797
+ ? `Created local public-review candidate: ${result.path}\nThis does not publish until a human submits it.`
798
+ : `Promotion blocked:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
799
+ },
800
+ ],
801
+ isError: !result.ok,
802
+ };
803
+ }
804
+ if (name === "kage_export_public_bundle") {
805
+ const result = (0, kernel_js_1.exportPublicBundle)(String(args?.project_dir ?? ""));
806
+ return {
807
+ content: [
808
+ {
809
+ type: "text",
810
+ text: result.ok
811
+ ? `Exported public bundle: ${result.path}\nPackets: ${result.packetCount}`
812
+ : `Public bundle blocked:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
813
+ },
814
+ ],
815
+ isError: !result.ok,
816
+ };
817
+ }
818
+ if (name === "kage_review_artifact") {
819
+ const result = (0, kernel_js_1.createReviewArtifact)(String(args?.project_dir ?? ""));
820
+ return {
821
+ content: [{ type: "text", text: `Wrote review artifact: ${result.path}\nPending packets: ${result.pending}` }],
822
+ };
823
+ }
824
+ if (name === "kage_propose_from_diff") {
825
+ const result = (0, kernel_js_1.proposeFromDiff)(String(args?.project_dir ?? ""));
826
+ return {
827
+ content: [
828
+ {
829
+ type: "text",
830
+ text: result.ok
831
+ ? `Wrote branch review summary: ${result.path}\nCaptured repo-local change memory: ${result.packetPath ?? "(none)"}\nChanged files: ${result.changedFiles.join(", ")}`
832
+ : `Proposal blocked:\n${result.errors.map((error) => `- ${error}`).join("\n")}`,
833
+ },
834
+ ],
835
+ isError: !result.ok,
836
+ };
837
+ }
188
838
  throw new Error(`Unknown tool: ${name}`);
839
+ }
840
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
841
+ const { name, arguments: args } = request.params;
842
+ return callTool(name, args);
189
843
  });
190
844
  async function main() {
191
845
  const transport = new stdio_js_1.StdioServerTransport();
192
846
  await server.connect(transport);
193
847
  }
194
- main().catch(console.error);
848
+ if (require.main === module) {
849
+ main().catch(console.error);
850
+ }