@vodailoc/kilo-kit-mcp 1.1.1 → 1.3.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.
@@ -1,27 +1,106 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFile } from "node:fs/promises";
3
+ import os from "node:os";
3
4
  import path from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
6
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
8
  import { z } from "zod";
8
- import { formatLoadedSkill, formatRoute, formatSkills, formatValidation, textResponse, } from "./formatters.js";
9
+ import { formatLoadedSkill, formatMemoryReport, formatOrchestration, formatRoute, formatRouteReport, formatSkills, formatValidation, textResponse, } from "./formatters.js";
10
+ import { createJsonlOrchestrationAudit, createNoopOrchestrationAudit } from "./orchestration-audit.js";
11
+ import { createSqliteOrchestrationMemory } from "./orchestration-memory.js";
12
+ import { createOrchestrator } from "./orchestrator.js";
9
13
  import { resolveInsideRepo } from "./paths.js";
14
+ import { createInMemoryRouteAnalytics, createJsonlRouteAnalytics } from "./route-analytics.js";
10
15
  import { createSkillRegistry } from "./registry.js";
11
16
  import { routeIntent } from "./router.js";
12
17
  import { validateSkills } from "./validator.js";
13
- const SERVER_VERSION = "1.1.1";
18
+ const SERVER_VERSION = "1.3.0";
14
19
  const DEFAULT_REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
15
20
  const formatSchema = z.enum(["markdown", "json"]).default("markdown");
16
21
  export async function createKiloKitServer(options = {}) {
17
22
  const repoRoot = path.resolve(options.repoRoot ?? process.env.KILO_KIT_REPO_ROOT ?? DEFAULT_REPO_ROOT);
18
23
  const registry = await createSkillRegistry({ repoRoot });
24
+ const routeAnalytics = process.env.KILO_KIT_WRITE_DECISIONS === "true"
25
+ ? createJsonlRouteAnalytics({
26
+ filePath: process.env.KILO_KIT_DECISION_TRAIL_PATH
27
+ ? path.resolve(process.env.KILO_KIT_DECISION_TRAIL_PATH)
28
+ : resolveInsideRepo(repoRoot, ".kilo/decision-trail.jsonl"),
29
+ })
30
+ : createInMemoryRouteAnalytics();
31
+ const orchestrationMemory = await createSqliteOrchestrationMemory({
32
+ filePath: path.resolve(process.env.KILO_KIT_MEMORY_PATH ?? path.join(os.homedir(), ".kilo-kit/orchestrator.sqlite")),
33
+ });
34
+ const orchestrationAudit = process.env.KILO_KIT_ORCHESTRATION_AUDIT_PATH
35
+ ? createJsonlOrchestrationAudit(path.resolve(process.env.KILO_KIT_ORCHESTRATION_AUDIT_PATH))
36
+ : createNoopOrchestrationAudit();
37
+ const orchestrator = createOrchestrator({
38
+ registry,
39
+ memory: orchestrationMemory,
40
+ audit: orchestrationAudit,
41
+ });
19
42
  const server = new McpServer({
20
43
  name: "kilo-kit",
21
44
  version: SERVER_VERSION,
22
45
  }, {
23
- instructions: "Use kilo_route_intent before selecting a Kilo-Kit workflow skill. Load one selected skill with kilo_get_skill, then follow its instructions. All tools are read-only.",
46
+ instructions: "Use kilo_route_intent before selecting a Kilo-Kit workflow skill. Load one selected skill with kilo_get_skill, then follow its instructions. Route telemetry is in-memory by default and only persists when KILO_KIT_WRITE_DECISIONS=true.",
24
47
  });
48
+ server.registerTool("kilo_orchestrate_task", {
49
+ title: "Kilo-Kit C4 Orchestrate Task",
50
+ description: "Central C4 orchestration gate. Routes internally, requires the real /brainstorming skill before substantive work, checks memory suggestions, and releases the post-brainstorming workflow after approval.",
51
+ inputSchema: {
52
+ message: z.string().min(1).max(4000).describe("Current user request or task summary."),
53
+ context: z
54
+ .object({
55
+ files: z.array(z.string().max(300)).max(30).optional(),
56
+ mode: z.string().max(80).optional(),
57
+ previousErrors: z.string().max(2000).optional(),
58
+ projectFingerprint: z.string().max(200).optional(),
59
+ })
60
+ .optional(),
61
+ sessionId: z.string().min(1).max(120).optional(),
62
+ brainstormingApproved: z.boolean().optional(),
63
+ answers: z.record(z.string().max(2000)).optional(),
64
+ memoryConfirmations: z.record(z.enum(["accepted", "rejected"])).optional(),
65
+ format: formatSchema.optional(),
66
+ },
67
+ annotations: {
68
+ readOnlyHint: true,
69
+ destructiveHint: false,
70
+ idempotentHint: false,
71
+ },
72
+ }, async ({ message, context, sessionId, brainstormingApproved, answers, memoryConfirmations, format }) => {
73
+ const result = orchestrator.orchestrate({
74
+ message,
75
+ ...(context
76
+ ? {
77
+ context: {
78
+ ...(context.files ? { files: context.files } : {}),
79
+ ...(context.mode ? { mode: context.mode } : {}),
80
+ ...(context.previousErrors ? { previousErrors: context.previousErrors } : {}),
81
+ ...(context.projectFingerprint ? { projectFingerprint: context.projectFingerprint } : {}),
82
+ },
83
+ }
84
+ : {}),
85
+ ...(sessionId ? { sessionId } : {}),
86
+ ...(brainstormingApproved !== undefined ? { brainstormingApproved } : {}),
87
+ ...(answers ? { answers } : {}),
88
+ ...(memoryConfirmations ? { memoryConfirmations } : {}),
89
+ });
90
+ return textResponse(formatOrchestration(result, normalizeFormat(format)));
91
+ });
92
+ server.registerTool("kilo_memory_report", {
93
+ title: "Kilo-Kit C4 Memory Report",
94
+ description: "Read global C4 memory facts, decisions, and recent suggestions.",
95
+ inputSchema: {
96
+ format: formatSchema.optional(),
97
+ },
98
+ annotations: {
99
+ readOnlyHint: true,
100
+ destructiveHint: false,
101
+ idempotentHint: true,
102
+ },
103
+ }, async ({ format }) => textResponse(formatMemoryReport(orchestrationMemory.report(), normalizeFormat(format))));
25
104
  server.registerTool("kilo_search_skills", {
26
105
  title: "Search Kilo-Kit Skills",
27
106
  description: "Search the Kilo-Kit skill library by natural-language query. Use this for broad discovery before loading a specific skill.",
@@ -99,9 +178,21 @@ export async function createKiloKitServer(options = {}) {
99
178
  message,
100
179
  ...(routeContext ? { context: routeContext } : {}),
101
180
  ...(limit ? { limit } : {}),
102
- });
181
+ }, { analytics: routeAnalytics });
103
182
  return textResponse(formatRoute(result, normalizeFormat(format)));
104
183
  });
184
+ server.registerTool("kilo_route_report", {
185
+ title: "Kilo-Kit Route Report",
186
+ description: "Summarize route telemetry: top skills, task modes, workflow chains, score averages, and conflict penalties.",
187
+ inputSchema: {
188
+ format: formatSchema.optional(),
189
+ },
190
+ annotations: {
191
+ readOnlyHint: true,
192
+ destructiveHint: false,
193
+ idempotentHint: true,
194
+ },
195
+ }, async ({ format }) => textResponse(formatRouteReport(routeAnalytics.report(), normalizeFormat(format))));
105
196
  server.registerTool("kilo_validate_skills", {
106
197
  title: "Validate Kilo-Kit Skills",
107
198
  description: "Run the Kilo-Kit skill validator and return a concise quality-gate summary. This is read-only and does not modify files.",
@@ -0,0 +1,18 @@
1
+ import { getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
2
+ const FORWARDED_ENV_VARS = [
3
+ "KILO_KIT_WRITE_DECISIONS",
4
+ "KILO_KIT_DECISION_TRAIL_PATH",
5
+ "KILO_KIT_REPO_ROOT",
6
+ "KILO_KIT_MEMORY_PATH",
7
+ "KILO_KIT_ORCHESTRATION_AUDIT_PATH",
8
+ ];
9
+ export function buildSmokeEnvironment(sourceEnv = process.env, baseEnv = getDefaultEnvironment()) {
10
+ const env = { ...baseEnv };
11
+ for (const name of FORWARDED_ENV_VARS) {
12
+ const value = sourceEnv[name];
13
+ if (value !== undefined) {
14
+ env[name] = value;
15
+ }
16
+ }
17
+ return env;
18
+ }
package/mcp/dist/smoke.js CHANGED
@@ -1,15 +1,24 @@
1
1
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
2
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ import { buildSmokeEnvironment } from "./smoke-env.js";
3
4
  const transport = new StdioClientTransport({
4
5
  command: process.env.KILO_KIT_SMOKE_COMMAND ?? process.execPath,
5
6
  args: parseSmokeArgs(),
7
+ env: buildSmokeEnvironment(),
6
8
  });
7
9
  const client = new Client({ name: "kilo-kit-smoke", version: "1.0.0" });
8
10
  try {
9
11
  await client.connect(transport);
10
12
  const tools = await client.listTools();
11
13
  const toolNames = tools.tools.map((tool) => tool.name);
12
- for (const required of ["kilo_route_intent", "kilo_get_skill", "kilo_validate_skills"]) {
14
+ for (const required of [
15
+ "kilo_route_intent",
16
+ "kilo_get_skill",
17
+ "kilo_validate_skills",
18
+ "kilo_route_report",
19
+ "kilo_orchestrate_task",
20
+ "kilo_memory_report",
21
+ ]) {
13
22
  if (!toolNames.includes(required)) {
14
23
  throw new Error(`Missing expected tool: ${required}`);
15
24
  }
@@ -26,6 +35,61 @@ try {
26
35
  if (!routeText.includes("engineering/tdd")) {
27
36
  throw new Error(`Smoke route did not recommend engineering/tdd: ${routeText}`);
28
37
  }
38
+ const report = await client.callTool({
39
+ name: "kilo_route_report",
40
+ arguments: { format: "json" },
41
+ });
42
+ const reportText = extractFirstText(report);
43
+ if (!reportText.includes('"totalEvents": 1') || !reportText.includes("engineering/tdd")) {
44
+ throw new Error(`Smoke route report did not include the routed event: ${reportText}`);
45
+ }
46
+ const orchestration = await client.callTool({
47
+ name: "kilo_orchestrate_task",
48
+ arguments: {
49
+ message: "Fix bug login, viết test trước",
50
+ context: { files: ["src/auth/login.ts"], mode: "coding", projectFingerprint: "smoke:typescript" },
51
+ format: "json",
52
+ },
53
+ });
54
+ const orchestrationText = extractFirstText(orchestration);
55
+ const orchestrationResult = JSON.parse(orchestrationText);
56
+ if (orchestrationResult.state !== "brainstorming_required") {
57
+ throw new Error(`Smoke orchestration did not require brainstorming: ${orchestrationText}`);
58
+ }
59
+ if (orchestrationResult.workflow?.[0]?.skill?.id !== "productivity/brainstorming") {
60
+ throw new Error(`Smoke orchestration did not start with brainstorming: ${orchestrationText}`);
61
+ }
62
+ if (orchestrationResult.firstSkillToLoad?.id !== "productivity/brainstorming") {
63
+ throw new Error(`Smoke orchestration did not instruct loading brainstorming: ${orchestrationText}`);
64
+ }
65
+ if ((orchestrationResult.questions?.length ?? 0) !== 0 || (orchestrationResult.missingInfo?.length ?? 0) !== 0) {
66
+ throw new Error(`Smoke orchestration should not use C4 questions as a gate: ${orchestrationText}`);
67
+ }
68
+ const readyOrchestration = await client.callTool({
69
+ name: "kilo_orchestrate_task",
70
+ arguments: {
71
+ message: "Fix bug login, viết test trước",
72
+ sessionId: orchestrationResult.sessionId,
73
+ brainstormingApproved: true,
74
+ format: "json",
75
+ },
76
+ });
77
+ const readyText = extractFirstText(readyOrchestration);
78
+ const readyResult = JSON.parse(readyText);
79
+ if (readyResult.state !== "ready" || readyResult.firstSkillToLoad?.id !== "engineering/diagnose") {
80
+ throw new Error(`Smoke orchestration did not release post-brainstorming workflow after approval: ${readyText}`);
81
+ }
82
+ if (readyResult.finalWorkflow?.some((step) => step.skill?.id === "productivity/brainstorming")) {
83
+ throw new Error(`Smoke final workflow should not repeat brainstorming after approval: ${readyText}`);
84
+ }
85
+ const memoryReport = await client.callTool({
86
+ name: "kilo_memory_report",
87
+ arguments: { format: "json" },
88
+ });
89
+ const memoryReportText = extractFirstText(memoryReport);
90
+ if (!memoryReportText.includes('"facts"')) {
91
+ throw new Error(`Smoke memory report did not return memory facts: ${memoryReportText}`);
92
+ }
29
93
  const skill = await client.callTool({
30
94
  name: "kilo_get_skill",
31
95
  arguments: { category: "engineering", skill: "tdd", maxChars: 800 },
package/mcp/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kilo-kit/mcp",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "Kilo-Kit MCP server for skill routing, skill loading, and validation.",
5
5
  "type": "module",
6
6
  "private": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vodailoc/kilo-kit-mcp",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "Kilo-Kit MCP server for adaptive skill routing, skill loading, and validation.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Kilo-Kit Team",