open-multi-agent-kit 0.78.6 → 0.78.7

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.
Files changed (169) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +26 -0
  3. package/MATURITY.md +2 -2
  4. package/README.md +19 -4
  5. package/dist/brand/night-city.theme.json +80 -0
  6. package/dist/brand/palette.d.ts +73 -177
  7. package/dist/brand/palette.js +115 -39
  8. package/dist/brand/rust-forge.theme.json +216 -0
  9. package/dist/brand/theme-compiled.d.ts +61 -0
  10. package/dist/brand/theme-compiled.js +241 -0
  11. package/dist/brand/theme.js +50 -37
  12. package/dist/cli/release-promotion-gate.d.ts +8 -1
  13. package/dist/cli/release-promotion-gate.js +20 -9
  14. package/dist/cli/root.d.ts +1 -0
  15. package/dist/cli/root.js +2 -1
  16. package/dist/cli/theme/index.d.ts +11 -2
  17. package/dist/cli/theme/index.js +6 -1
  18. package/dist/cli/theme/oklab-quantize.d.ts +29 -0
  19. package/dist/cli/theme/oklab-quantize.js +105 -0
  20. package/dist/cli/theme/render-table.d.ts +59 -0
  21. package/dist/cli/theme/render-table.js +112 -0
  22. package/dist/cli/theme/status-frame.d.ts +10 -0
  23. package/dist/cli/theme/status-frame.js +22 -0
  24. package/dist/cli/theme/terminal-capability.d.ts +6 -1
  25. package/dist/cli/theme/terminal-capability.js +20 -4
  26. package/dist/cli/theme/theme-doc.d.ts +22 -0
  27. package/dist/cli/theme/theme-doc.js +97 -0
  28. package/dist/cli/theme/theme-registry.js +39 -18
  29. package/dist/cli/theme/tier-explain.d.ts +21 -0
  30. package/dist/cli/theme/tier-explain.js +60 -0
  31. package/dist/cli/ui/green-rain-renderer.js +3 -2
  32. package/dist/cli/ui/neon-grid-renderer.js +4 -3
  33. package/dist/cli/ui/rust-forge-renderer.js +17 -3
  34. package/dist/cli/ui/system24-renderer.d.ts +18 -1
  35. package/dist/cli/ui/system24-renderer.js +39 -16
  36. package/dist/cli/v2/chat-repl.js +3 -1
  37. package/dist/cli/v2/cli-v2-skeleton.d.ts +6 -0
  38. package/dist/cli/v2/cli-v2-skeleton.js +121 -9
  39. package/dist/cli/v2/interactive-prompt.d.ts +1 -0
  40. package/dist/cli/v2/interactive-prompt.js +6 -2
  41. package/dist/commands/chat/core.js +2 -2
  42. package/dist/commands/chat/runtime.js +2 -0
  43. package/dist/commands/chat/utils.js +12 -4
  44. package/dist/commands/cockpit/render.js +102 -64
  45. package/dist/commands/cockpit/utils.d.ts +2 -0
  46. package/dist/commands/design.js +18 -16
  47. package/dist/commands/doctor/checks.d.ts +0 -1
  48. package/dist/commands/doctor/checks.js +1 -36
  49. package/dist/commands/doctor/report.js +0 -11
  50. package/dist/commands/init/content.d.ts +1 -1
  51. package/dist/commands/init/content.js +3 -11
  52. package/dist/commands/init.js +2 -2
  53. package/dist/commands/mcp/config.d.ts +32 -0
  54. package/dist/commands/mcp/config.js +264 -0
  55. package/dist/commands/mcp/doctor-fix.d.ts +12 -0
  56. package/dist/commands/mcp/doctor-fix.js +261 -0
  57. package/dist/commands/mcp/doctor.d.ts +55 -0
  58. package/dist/commands/mcp/doctor.js +401 -0
  59. package/dist/commands/mcp/list.d.ts +1 -0
  60. package/dist/commands/mcp/list.js +56 -0
  61. package/dist/commands/mcp/shared.d.ts +47 -0
  62. package/dist/commands/mcp/shared.js +192 -0
  63. package/dist/commands/mcp/test.d.ts +5 -0
  64. package/dist/commands/mcp/test.js +457 -0
  65. package/dist/commands/mcp.d.ts +8 -104
  66. package/dist/commands/mcp.js +5 -1603
  67. package/dist/contracts/proof.d.ts +1 -1
  68. package/dist/goal/control-loop.d.ts +15 -0
  69. package/dist/goal/control-loop.js +39 -5
  70. package/dist/hud/render.d.ts +2 -0
  71. package/dist/hud/render.js +7 -1
  72. package/dist/mcp/secret-scanner.d.ts +6 -0
  73. package/dist/mcp/secret-scanner.js +44 -11
  74. package/dist/memory/graph-delta-log.d.ts +137 -0
  75. package/dist/memory/graph-delta-log.js +650 -0
  76. package/dist/memory/graph-viewer.js +13 -32
  77. package/dist/memory/local-graph-memory-store.d.ts +36 -3
  78. package/dist/memory/local-graph-memory-store.js +255 -10
  79. package/dist/orchestration/log-streamer.js +13 -8
  80. package/dist/orchestration/routing.d.ts +4 -0
  81. package/dist/orchestration/routing.js +28 -3
  82. package/dist/orchestration/state-persister.js +8 -1
  83. package/dist/providers/anthropic-messages-runner.d.ts +17 -0
  84. package/dist/providers/anthropic-messages-runner.js +197 -0
  85. package/dist/providers/model-registry.d.ts +1 -1
  86. package/dist/providers/model-registry.js +37 -3
  87. package/dist/providers/provider-runtime.js +27 -11
  88. package/dist/providers/thinking-levels.js +4 -1
  89. package/dist/providers/types.d.ts +3 -3
  90. package/dist/runtime/contracts/weakness-remediation.d.ts +9 -1
  91. package/dist/runtime/contracts/weakness-remediation.js +9 -1
  92. package/dist/runtime/router-v2-scoring.d.ts +8 -3
  93. package/dist/runtime/router-v2-scoring.js +27 -22
  94. package/dist/runtime/runtime-router.d.ts +5 -0
  95. package/dist/runtime/runtime-router.js +40 -8
  96. package/dist/runtime/sandbox-profile.d.ts +29 -0
  97. package/dist/runtime/sandbox-profile.js +101 -0
  98. package/dist/runtime/slash-commands.js +300 -2
  99. package/dist/runtime/tool-dispatch-contracts.d.ts +15 -0
  100. package/dist/runtime/tool-dispatch-contracts.js +12 -0
  101. package/dist/runtime/weights-config.d.ts +90 -0
  102. package/dist/runtime/weights-config.js +249 -0
  103. package/dist/schema/envelope.schema.d.ts +18 -18
  104. package/dist/schema/evidence.schema.d.ts +2 -2
  105. package/dist/schema/proof-bundle.schema.d.ts +6 -6
  106. package/dist/schema/proof-bundle.schema.js +0 -1
  107. package/dist/schema/provider.schema.d.ts +2 -2
  108. package/dist/schema/run-manifest.schema.d.ts +16 -16
  109. package/dist/theme/ansi.js +9 -2
  110. package/dist/theme/extended-palette.d.ts +85 -0
  111. package/dist/theme/extended-palette.js +108 -0
  112. package/dist/theme/layout.js +29 -8
  113. package/dist/theme/metrics.js +5 -5
  114. package/dist/theme/parallel.js +25 -15
  115. package/dist/ui/omk-sigil.js +5 -14
  116. package/dist/ui/omk-working-sweep.js +5 -15
  117. package/dist/util/chat-agent-mode.d.ts +11 -0
  118. package/dist/util/chat-agent-mode.js +41 -8
  119. package/dist/util/chat-cockpit.d.ts +9 -0
  120. package/dist/util/chat-cockpit.js +77 -20
  121. package/dist/util/fs/core.d.ts +5 -0
  122. package/dist/util/fs/core.js +35 -0
  123. package/dist/util/fs/internal.d.ts +14 -0
  124. package/dist/util/fs/internal.js +169 -0
  125. package/dist/util/fs/kimi-sync.d.ts +41 -0
  126. package/dist/util/fs/kimi-sync.js +495 -0
  127. package/dist/util/fs/logo.d.ts +2 -0
  128. package/dist/util/fs/logo.js +78 -0
  129. package/dist/util/fs/manifest.d.ts +6 -0
  130. package/dist/util/fs/manifest.js +40 -0
  131. package/dist/util/fs/mcp-diagnose.d.ts +15 -0
  132. package/dist/util/fs/mcp-diagnose.js +204 -0
  133. package/dist/util/fs/mcp-runtime-config.d.ts +23 -0
  134. package/dist/util/fs/mcp-runtime-config.js +412 -0
  135. package/dist/util/fs/paths.d.ts +13 -0
  136. package/dist/util/fs/paths.js +82 -0
  137. package/dist/util/fs/preflight.d.ts +27 -0
  138. package/dist/util/fs/preflight.js +271 -0
  139. package/dist/util/fs.d.ts +8 -128
  140. package/dist/util/fs.js +8 -1757
  141. package/dist/util/terminal-layout.js +5 -1
  142. package/dist/util/version.js +2 -1
  143. package/docs/2026-06-11/critical-issues.md +20 -0
  144. package/docs/2026-06-11/improvements.md +14 -0
  145. package/docs/2026-06-11/init-checklist.md +25 -0
  146. package/docs/2026-06-11/plan.md +20 -0
  147. package/docs/ARCHITECTURE_ANALYSIS_CODEGRAPH.md +223 -0
  148. package/docs/adr/0001-no-native-rust-lane.md +52 -0
  149. package/docs/codex-oauth-setup.md +14 -0
  150. package/docs/decisions/2026-06-10-git-history-bloat.md +61 -0
  151. package/docs/decisions/ADR-theme-dark-only-assets.md +43 -0
  152. package/docs/getting-started.md +1 -1
  153. package/docs/headroom-omk-final-summary.md +188 -0
  154. package/docs/headroom-omk-integration.md +394 -0
  155. package/docs/headroom-omk-setup-guide.md +422 -0
  156. package/docs/headroom-omk-usage-examples.md +371 -0
  157. package/docs/provider-maturity.md +1 -1
  158. package/docs/versioning.md +3 -3
  159. package/package.json +10 -10
  160. package/readmeasset/.npmignore +2 -0
  161. package/readmeasset/ASSET_INDEX.md +7 -5
  162. package/readmeasset/ASSET_PROVENANCE.md +21 -5
  163. package/readmeasset/omk-control-surfaces.svg +55 -0
  164. package/readmeasset/omk-core-loop.svg +1 -0
  165. package/readmeasset/omk-logo-mark.svg +1 -0
  166. package/readmeasset/omk-release-assertions.svg +51 -0
  167. package/dist/native/linux-x64/omk-safety +0 -0
  168. package/dist/util/native-safety.d.ts +0 -28
  169. package/dist/util/native-safety.js +0 -118
@@ -0,0 +1,55 @@
1
+ import { type McpDoctorFixReport } from "./doctor-fix.js";
2
+ export interface McpDoctorOptions {
3
+ json?: boolean;
4
+ fix?: boolean;
5
+ dryRun?: boolean;
6
+ global?: boolean;
7
+ }
8
+ type McpDoctorSeverity = "ok" | "info" | "warn" | "error";
9
+ export interface McpDoctorCheck {
10
+ severity: McpDoctorSeverity;
11
+ kind: string;
12
+ message: string;
13
+ }
14
+ export interface McpDoctorSourceReport {
15
+ path: string;
16
+ exists: boolean;
17
+ active: boolean;
18
+ parsed: boolean;
19
+ status: "ok" | "empty" | "error";
20
+ serverCount: number;
21
+ error?: string;
22
+ }
23
+ export interface McpDoctorServerReport {
24
+ name: string;
25
+ status: "ok" | "warn" | "error";
26
+ active: boolean;
27
+ sources: string[];
28
+ activeSources: string[];
29
+ transport: "remote" | "stdio" | "invalid";
30
+ url?: string;
31
+ command?: string;
32
+ resolvedCommand?: string;
33
+ timeoutSec?: number;
34
+ checks: McpDoctorCheck[];
35
+ }
36
+ export interface McpDoctorReport {
37
+ ok: boolean;
38
+ command: "mcp doctor";
39
+ checkedAt: string;
40
+ activeScope: string;
41
+ issueCount: number;
42
+ errors: string[];
43
+ warnings: string[];
44
+ fixes?: McpDoctorFixReport;
45
+ sources: McpDoctorSourceReport[];
46
+ servers: McpDoctorServerReport[];
47
+ data: {
48
+ activeScope: string;
49
+ sourceCount: number;
50
+ serverCount: number;
51
+ };
52
+ }
53
+ export declare function mcpDoctorCommand(options?: McpDoctorOptions): Promise<void>;
54
+ export declare function buildMcpDoctorReport(): Promise<McpDoctorReport>;
55
+ export {};
@@ -0,0 +1,401 @@
1
+ import { join, isAbsolute } from "path";
2
+ import { which } from "../../util/shell.js";
3
+ import { collectMcpConfigs, diagnoseRuntimeMcpServer, pathExists, getUserHome, preflightRuntimeMcpServers, resolveRuntimeMcpPreflightOptions, } from "../../util/fs.js";
4
+ import { getOmkResourceSettings } from "../../util/resource-profile.js";
5
+ import { style, header, status, label } from "../../util/theme.js";
6
+ import { maskSensitiveText } from "../../util/secret-mask.js";
7
+ import { basenameOfCommand, collectServers, isHttpUrl, isRailwayMcpServer, isSupabaseMcpServer, RAILWAY_REMOTE_MCP_URL, resolveAllConfigs, sanitizeMcpUrlForDisplay, selectEffectiveServer, serverTargetText, validateRemoteMcpUrl, } from "./shared.js";
8
+ import { repairMcpDoctorIssues } from "./doctor-fix.js";
9
+ export async function mcpDoctorCommand(options = {}) {
10
+ const fixes = options.fix
11
+ ? await repairMcpDoctorIssues({
12
+ dryRun: Boolean(options.dryRun),
13
+ global: Boolean(options.global),
14
+ })
15
+ : undefined;
16
+ const report = await buildMcpDoctorReport();
17
+ if (fixes)
18
+ report.fixes = fixes;
19
+ if (options.json) {
20
+ console.log(JSON.stringify(report, null, 2));
21
+ if (!report.ok)
22
+ process.exitCode = 1;
23
+ return;
24
+ }
25
+ emitMcpDoctorText(report);
26
+ if (!report.ok)
27
+ process.exitCode = 1;
28
+ }
29
+ export async function buildMcpDoctorReport() {
30
+ const sources = await resolveAllConfigs();
31
+ const servers = collectServers(sources);
32
+ const resources = await getOmkResourceSettings();
33
+ const activePathOrder = await collectMcpConfigs(resources.mcpScope);
34
+ const activePaths = new Set(activePathOrder);
35
+ const globalMcpPath = join(getUserHome(), ".kimi", "mcp.json");
36
+ const preflightEntriesByName = new Map();
37
+ const preflightOptions = resolveRuntimeMcpPreflightOptions();
38
+ if (preflightOptions.mode !== "off") {
39
+ const activeServersForPreflight = {};
40
+ for (const [name, info] of servers) {
41
+ const activeSources = info.sources.filter((source) => activePaths.has(source));
42
+ if (activeSources.length === 0)
43
+ continue;
44
+ activeServersForPreflight[name] = selectEffectiveServer(info, activePathOrder);
45
+ }
46
+ const preflight = await preflightRuntimeMcpServers(activeServersForPreflight, preflightOptions);
47
+ for (const entry of preflight.entries) {
48
+ preflightEntriesByName.set(entry.name, entry);
49
+ }
50
+ }
51
+ const sourceReports = sources.map((src) => {
52
+ const serverCount = src.parsed ? Object.keys(src.config.mcpServers ?? {}).length : 0;
53
+ const sourceReport = {
54
+ path: src.path,
55
+ exists: src.exists,
56
+ active: activePaths.has(src.path),
57
+ parsed: src.parsed,
58
+ status: !src.parsed ? "error" : !src.exists || serverCount === 0 ? "empty" : "ok",
59
+ serverCount,
60
+ error: src.error ? maskSensitiveText(src.error) : undefined,
61
+ };
62
+ return sourceReport;
63
+ });
64
+ const serverReports = [];
65
+ for (const [name, info] of servers) {
66
+ const server = selectEffectiveServer(info, activePathOrder);
67
+ const checks = [];
68
+ const activeSources = info.sources.filter((source) => activePaths.has(source));
69
+ const active = activeSources.length > 0;
70
+ let resolvedCommand;
71
+ if (activeSources.length > 1) {
72
+ if (name === "omk-project") {
73
+ checks.push({
74
+ severity: "info",
75
+ kind: "managed-duplicate",
76
+ message: `managed omk-project mirror duplicate: ${activeSources.join(", ")} (expected; remove one manually if desired)`,
77
+ });
78
+ }
79
+ else if (activeSources.includes(globalMcpPath) && activeSources.some((source) => source !== globalMcpPath)) {
80
+ checks.push({
81
+ severity: "info",
82
+ kind: "project-overrides-global",
83
+ message: `active duplicate spans global/project; project override is effective: ${activeSources.join(", ")}`,
84
+ });
85
+ }
86
+ else {
87
+ checks.push({
88
+ severity: "error",
89
+ kind: "active-duplicate",
90
+ message: `active duplicate definition in: ${activeSources.join(", ")} — run \`omk mcp remove ${name}\` to delete the local copy, or edit the files manually`,
91
+ });
92
+ }
93
+ }
94
+ else if (info.sources.length > 1) {
95
+ checks.push({
96
+ severity: "info",
97
+ kind: "inactive-duplicate",
98
+ message: `duplicate mirror outside active scope: ${info.sources.join(", ")} (only the active scope config is used)`,
99
+ });
100
+ }
101
+ if (!active) {
102
+ serverReports.push({
103
+ name,
104
+ status: "ok",
105
+ active,
106
+ sources: info.sources,
107
+ activeSources,
108
+ transport: server.url ? "remote" : server.command ? "stdio" : "invalid",
109
+ url: server.url ? sanitizeMcpUrlForDisplay(server.url) : undefined,
110
+ command: server.command ? maskSensitiveText(server.command) : undefined,
111
+ resolvedCommand,
112
+ timeoutSec: server.startup_timeout_sec,
113
+ checks,
114
+ });
115
+ continue;
116
+ }
117
+ if (server.url) {
118
+ const urlCheck = validateRemoteMcpUrl(server.url);
119
+ if (urlCheck.ok) {
120
+ checks.push({ severity: "ok", kind: "url", message: `url: ${sanitizeMcpUrlForDisplay(server.url)}` });
121
+ }
122
+ else {
123
+ checks.push({ severity: "error", kind: "invalid-url", message: `invalid url: ${maskSensitiveText(urlCheck.message)}` });
124
+ }
125
+ }
126
+ else if (!server.command) {
127
+ checks.push({ severity: "error", kind: "missing-command", message: "missing command" });
128
+ }
129
+ else {
130
+ const resolved = await which(server.command);
131
+ if (resolved.failed) {
132
+ checks.push({ severity: "error", kind: "command-not-found", message: `command not found: ${maskSensitiveText(server.command)}` });
133
+ }
134
+ else {
135
+ resolvedCommand = resolved.stdout.trim();
136
+ checks.push({ severity: "ok", kind: "command", message: `command: ${maskSensitiveText(resolvedCommand)}` });
137
+ }
138
+ }
139
+ if (!server.url && server.args) {
140
+ for (const [index, arg] of server.args.entries()) {
141
+ if (!shouldValidateArgPath(server, arg, index))
142
+ continue;
143
+ if (!isAbsolute(arg) && !arg.includes("/") && !arg.includes("\\"))
144
+ continue;
145
+ const exists = await pathExists(arg);
146
+ const displayArg = maskSensitiveText(arg);
147
+ checks.push(exists
148
+ ? { severity: "ok", kind: "arg-path", message: `arg path: ${displayArg}` }
149
+ : { severity: "error", kind: "arg-path-not-found", message: `arg path not found: ${displayArg}` });
150
+ }
151
+ }
152
+ if (server.env) {
153
+ for (const [key, value] of Object.entries(server.env)) {
154
+ if (value.startsWith("${") && value.endsWith("}")) {
155
+ const envName = value.slice(2, -1);
156
+ if (!process.env[envName]) {
157
+ checks.push({ severity: "warn", kind: "env-reference-undefined", message: `env reference undefined: ${key} → ${envName}` });
158
+ }
159
+ }
160
+ }
161
+ }
162
+ for (const check of stdioProtocolChecksForServer(server)) {
163
+ checks.push(check);
164
+ }
165
+ const runtimeDiagnostics = await diagnoseRuntimeMcpServer(name, server);
166
+ for (const diagnostic of runtimeDiagnostics) {
167
+ if (["missing-command", "command-path-not-found", "arg-path-not-found"].includes(diagnostic.kind))
168
+ continue;
169
+ checks.push({
170
+ severity: diagnostic.kind === "disabled-server" ? "info" : "error",
171
+ kind: `runtime-${diagnostic.kind}`,
172
+ message: `runtime startup blocker: ${diagnostic.message}`,
173
+ });
174
+ }
175
+ const stabilityHints = stabilityHintsForServer(name, server);
176
+ for (const hint of stabilityHints) {
177
+ checks.push({ severity: "info", kind: "stability", message: `stability: ${hint}` });
178
+ }
179
+ const preflightEntry = preflightEntriesByName.get(name);
180
+ if (preflightEntry?.status === "failed") {
181
+ checks.push(...preflightChecksForDoctor(preflightEntry));
182
+ }
183
+ const hasError = checks.some((check) => check.severity === "error");
184
+ const hasWarn = checks.some((check) => check.severity === "warn");
185
+ serverReports.push({
186
+ name,
187
+ status: hasError ? "error" : hasWarn ? "warn" : "ok",
188
+ active,
189
+ sources: info.sources,
190
+ activeSources,
191
+ transport: server.url ? "remote" : server.command ? "stdio" : "invalid",
192
+ url: server.url ? sanitizeMcpUrlForDisplay(server.url) : undefined,
193
+ command: server.command ? maskSensitiveText(server.command) : undefined,
194
+ resolvedCommand,
195
+ timeoutSec: server.startup_timeout_sec,
196
+ checks,
197
+ });
198
+ }
199
+ if (resources.mcpScope !== "none" && !servers.has("omk-project")) {
200
+ serverReports.push({
201
+ name: "omk-project",
202
+ status: "ok",
203
+ active: true,
204
+ sources: ["runtime:auto-injected"],
205
+ activeSources: ["runtime:auto-injected"],
206
+ transport: "stdio",
207
+ command: "omk mcp serve omk-project",
208
+ checks: [{
209
+ severity: "info",
210
+ kind: "virtual-runtime-injected",
211
+ message: "virtual runtime MCP injected; not stored in .kimi/mcp.json or .omk/mcp.json",
212
+ }],
213
+ });
214
+ }
215
+ const errors = [
216
+ ...sourceReports
217
+ .filter((source) => source.status === "error" && activePaths.has(source.path))
218
+ .map((source) => maskSensitiveText(`${source.path}: ${source.error ?? "invalid MCP config"}`)),
219
+ ...serverReports.flatMap((server) => server.checks
220
+ .filter((check) => check.severity === "error")
221
+ .map((check) => maskSensitiveText(`${server.name}: ${check.message}`))),
222
+ ];
223
+ const warnings = serverReports.flatMap((server) => server.checks
224
+ .filter((check) => check.severity === "warn")
225
+ .map((check) => maskSensitiveText(`${server.name}: ${check.message}`)));
226
+ return {
227
+ ok: errors.length === 0,
228
+ command: "mcp doctor",
229
+ checkedAt: new Date().toISOString(),
230
+ activeScope: resources.mcpScope,
231
+ issueCount: errors.length,
232
+ errors,
233
+ warnings,
234
+ sources: sourceReports,
235
+ servers: serverReports,
236
+ data: {
237
+ activeScope: resources.mcpScope,
238
+ sourceCount: sourceReports.length,
239
+ serverCount: serverReports.length,
240
+ },
241
+ };
242
+ }
243
+ function emitMcpDoctorText(report) {
244
+ console.log(header("MCP Doctor"));
245
+ console.log(style.gray(`Active MCP scope: ${report.activeScope}`));
246
+ for (const src of report.sources) {
247
+ if (src.status === "error") {
248
+ const line = `${src.path}: ${src.error}`;
249
+ console.log(src.active ? status.error(line) : style.gray(`${line} (inactive)`));
250
+ }
251
+ else if (src.status === "empty") {
252
+ const suffix = !src.exists ? "not found" : "no mcpServers defined";
253
+ console.log(style.gray(`${src.path}: ${suffix}${src.active ? "" : " (inactive)"}`));
254
+ }
255
+ else {
256
+ console.log(src.active ? status.ok(`${src.path}`) : style.gray(`${src.path} (inactive)`));
257
+ }
258
+ }
259
+ if (report.servers.length === 0) {
260
+ console.log("\n" + style.gray("No servers to diagnose."));
261
+ }
262
+ else {
263
+ console.log("");
264
+ for (const server of report.servers) {
265
+ console.log(label("Server", server.name));
266
+ for (const check of server.checks) {
267
+ console.log(` ${formatMcpDoctorCheck(check)}`);
268
+ }
269
+ }
270
+ }
271
+ if (report.fixes) {
272
+ console.log("");
273
+ console.log(label("Fix mode", report.fixes.dryRun ? "dry-run" : report.fixes.global ? "project+global" : "project-local"));
274
+ for (const action of report.fixes.actions) {
275
+ console.log(` ${report.fixes.dryRun ? style.skin("•") : style.mint("✓")} ${action}`);
276
+ }
277
+ for (const backup of report.fixes.backups) {
278
+ console.log(` ${style.gray("backup:")} ${backup}`);
279
+ }
280
+ for (const item of report.fixes.skipped) {
281
+ console.log(` ${style.gray("skipped:")} ${item}`);
282
+ }
283
+ }
284
+ console.log("");
285
+ if (report.ok) {
286
+ console.log(status.ok("All checks passed"));
287
+ }
288
+ else {
289
+ console.log(status.error(`${report.issueCount} issue(s) found`));
290
+ console.log(style.gray("To resolve duplicates: run `omk mcp remove <server>` to delete a local copy, or edit the config files directly."));
291
+ }
292
+ }
293
+ function formatMcpDoctorCheck(check) {
294
+ switch (check.severity) {
295
+ case "ok":
296
+ return `${style.mint("✓")} ${check.message}`;
297
+ case "warn":
298
+ return `${style.skin("⚠")} ${check.message}`;
299
+ case "error":
300
+ return `${style.pink("✗")} ${check.message}`;
301
+ default:
302
+ return `${style.gray("ℹ")} ${check.message}`;
303
+ }
304
+ }
305
+ function preflightChecksForDoctor(entry) {
306
+ const packageNote = entry.packageSpec ? ` (${maskSensitiveText(entry.packageSpec)})` : "";
307
+ const detail = entry.detail ? `: ${maskSensitiveText(entry.detail)}` : "";
308
+ const failureKind = entry.reason === "timeout" ? "preflight-timeout" : "preflight-package-unavailable";
309
+ const failureMessage = entry.reason === "timeout"
310
+ ? `warn: handshake-timeout/package preflight timed out${packageNote}; run \`omk mcp check --all\` (or \`omk mcp prewarm --all\`) to check caches`
311
+ : `npm-family package resolution failed${packageNote}${detail}; run \`omk mcp check --all\` (or \`omk mcp prewarm --all\`) to check caches`;
312
+ return [
313
+ { severity: "warn", kind: failureKind, message: failureMessage },
314
+ {
315
+ severity: "warn",
316
+ kind: "prewarm-needed",
317
+ message: "preflight check recommended before chat startup; for one server run `omk mcp check <server-name>`",
318
+ },
319
+ ];
320
+ }
321
+ function stabilityHintsForServer(name, server) {
322
+ if (!server.command && !server.url)
323
+ return [];
324
+ const hints = [];
325
+ const target = serverTargetText(server);
326
+ if ((server.command?.includes("npx") ?? false) || (server.command?.includes("npm") ?? false)) {
327
+ hints.push("npx-based servers may take >10s to start on first run. Consider installing globally or pinning the package.");
328
+ }
329
+ if (isRailwayMcpServer(name, server)) {
330
+ if (server.url?.includes("mcp.railway.com")) {
331
+ hints.push("Railway remote MCP uses OAuth over HTTP and avoids local npx/CLI token files; the first tool call may open browser auth.");
332
+ }
333
+ else if (target.includes("@jasontanswe/railway-mcp")) {
334
+ hints.push("Unofficial Railway MCP uses RAILWAY_API_TOKEN and calls the GraphQL API directly (no CLI needed). If projects return empty, verify your token has project access at https://railway.app/account/tokens.");
335
+ }
336
+ else {
337
+ hints.push("Railway CLI tokens expire periodically. Run railway login to refresh before using this server.");
338
+ if (target.includes("@railway/mcp-server")) {
339
+ hints.push(`Railway local MCP depends on Railway CLI auth and npx cold starts. Prefer the remote OAuth preset: omk mcp install railway or Codex: codex mcp add railway --url ${RAILWAY_REMOTE_MCP_URL}.`);
340
+ }
341
+ else {
342
+ hints.push(`Railway MCP is most stable as a remote OAuth server at ${RAILWAY_REMOTE_MCP_URL}; avoid committing API tokens into MCP JSON.`);
343
+ }
344
+ }
345
+ }
346
+ if (isSupabaseMcpServer(name, server)) {
347
+ hints.push("Supabase MCP requires an access token. Store it in your global MCP env (not committed) and run omk mcp doctor to verify. Tokens expire; refresh them from the Supabase account dashboard.");
348
+ }
349
+ if (name === "promptfoo") {
350
+ hints.push("promptfoo can be slow to initialize. Ensure NODE_OPTIONS does not limit memory, or run 'omk mcp test promptfoo' with a longer timeout.");
351
+ }
352
+ if (name === "obsidian") {
353
+ hints.push("obsidian MCP requires an active Obsidian vault and the Local REST API plugin. If the vault is closed, the server will fail.");
354
+ }
355
+ return hints;
356
+ }
357
+ function stdioProtocolChecksForServer(server) {
358
+ if (server.url)
359
+ return [];
360
+ const args = server.args ?? [];
361
+ if (args.includes("@modelcontextprotocol/server-pdf") && !args.includes("--stdio")) {
362
+ return [{
363
+ severity: "error",
364
+ kind: "stdio-protocol-mismatch",
365
+ message: "@modelcontextprotocol/server-pdf defaults to HTTP and can write 'MCP server listening on .../mcp' to stdout; add --stdio or configure it as a remote URL",
366
+ }];
367
+ }
368
+ return [];
369
+ }
370
+ function shouldValidateArgPath(server, arg, index) {
371
+ if (typeof arg !== "string" || arg.startsWith("-") || arg.startsWith("$"))
372
+ return false;
373
+ if (isShellInlineScript(server, index))
374
+ return false;
375
+ if (isNpxPackageSpecifier(server, arg))
376
+ return false;
377
+ if (isHttpUrl(arg))
378
+ return false;
379
+ // Shell snippets, inline commands, and arguments with whitespace are not
380
+ // standalone filesystem paths. Validating them as paths creates false MCP
381
+ // doctor failures for commands like `bash -lc "exec node /path/server.js"`.
382
+ if (/[\s;"'|&<>]/.test(arg))
383
+ return false;
384
+ return true;
385
+ }
386
+ function isNpxPackageSpecifier(server, arg) {
387
+ const command = basenameOfCommand(server.command ?? "");
388
+ if (!["npx", "npm", "pnpm", "yarn", "bun"].includes(command))
389
+ return false;
390
+ if (arg.startsWith(".") || arg.startsWith("/") || arg.includes("\\") || arg.includes(":"))
391
+ return false;
392
+ return /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+(?:@[a-z0-9._~+-]+)?$/i.test(arg);
393
+ }
394
+ function isShellInlineScript(server, index) {
395
+ const command = basenameOfCommand(server.command ?? "");
396
+ if (!["bash", "sh", "zsh", "fish", "pwsh", "powershell", "cmd", "cmd.exe"].includes(command)) {
397
+ return false;
398
+ }
399
+ const previous = server.args?.[index - 1];
400
+ return previous === "-c" || previous === "-lc" || previous === "/c" || previous === "--command";
401
+ }
@@ -0,0 +1 @@
1
+ export declare function mcpListCommand(): Promise<void>;
@@ -0,0 +1,56 @@
1
+ import { collectMcpConfigs } from "../../util/fs.js";
2
+ import { getOmkResourceSettings } from "../../util/resource-profile.js";
3
+ import { style, header, bullet } from "../../util/theme.js";
4
+ import { maskSensitiveText } from "../../util/secret-mask.js";
5
+ import { collectServers, formatArgsForDisplay, resolveAllConfigs, sanitizeMcpUrlForDisplay, selectEffectiveServer, } from "./shared.js";
6
+ export async function mcpListCommand() {
7
+ const sources = await resolveAllConfigs();
8
+ const servers = collectServers(sources);
9
+ const resources = await getOmkResourceSettings();
10
+ const activePathOrder = await collectMcpConfigs(resources.mcpScope);
11
+ const activePaths = new Set(activePathOrder);
12
+ console.log(header("MCP Servers"));
13
+ for (const src of sources) {
14
+ const icon = !src.exists ? style.gray("-") : src.parsed ? style.mint("✓") : style.pink("✗");
15
+ const marker = activePaths.has(src.path) ? style.mint(" [active]") : style.gray(" [inactive]");
16
+ const missing = !src.exists ? style.gray(" (not found)") : "";
17
+ console.log(`${icon} ${src.path}${marker}${missing}`);
18
+ if (src.error)
19
+ console.log(` ${style.gray(maskSensitiveText(src.error))}`);
20
+ }
21
+ if (servers.size === 0) {
22
+ console.log("\n" + style.gray("No MCP servers configured."));
23
+ return;
24
+ }
25
+ console.log("");
26
+ let duplicateCount = 0;
27
+ for (const [name, info] of servers) {
28
+ const server = selectEffectiveServer(info, activePathOrder);
29
+ const activeSources = info.sources.filter((source) => activePaths.has(source));
30
+ const dup = info.sources.length > 1 ? style.skin(` [duplicate: ${info.sources.length} sources]`) : "";
31
+ if (info.sources.length > 1)
32
+ duplicateCount++;
33
+ const activeMarker = activeSources.length > 0 ? style.mint(" [active]") : style.gray(" [inactive]");
34
+ console.log(bullet(`${style.purpleBold(name)}${dup}${activeMarker}`, "purple"));
35
+ if (server.url) {
36
+ console.log(` ${style.gray("url:")} ${sanitizeMcpUrlForDisplay(server.url)}`);
37
+ }
38
+ if (server.command || !server.url) {
39
+ console.log(` ${style.gray("command:")} ${server.command ? maskSensitiveText(server.command) : style.pink("missing")}`);
40
+ }
41
+ if (server.args && server.args.length > 0) {
42
+ console.log(` ${style.gray("args:")} ${formatArgsForDisplay(server.args)}`);
43
+ }
44
+ if (server.env && Object.keys(server.env).length > 0) {
45
+ console.log(` ${style.gray("env:")} ${Object.keys(server.env).join(", ")}`);
46
+ }
47
+ console.log(` ${style.gray("from:")} ${info.sources.join(", ")}`);
48
+ if (activeSources.length > 0) {
49
+ console.log(` ${style.gray("active from:")} ${activeSources.join(", ")}`);
50
+ }
51
+ }
52
+ if (duplicateCount > 0) {
53
+ console.log("");
54
+ console.log(style.skin(`⚠ ${duplicateCount} duplicate server(s) detected. Run \`omk mcp doctor\` for details, or \`omk mcp remove <name>\` to delete a local copy.`));
55
+ }
56
+ }
@@ -0,0 +1,47 @@
1
+ export interface McpServerConfig {
2
+ url?: string;
3
+ command?: string;
4
+ args?: string[];
5
+ env?: Record<string, string>;
6
+ headers?: Record<string, string>;
7
+ http_headers?: Record<string, string>;
8
+ startup_timeout_sec?: number;
9
+ enabled?: boolean;
10
+ }
11
+ export interface McpConfig {
12
+ mcpServers?: Record<string, McpServerConfig>;
13
+ }
14
+ export interface ConfigSource {
15
+ path: string;
16
+ config: McpConfig;
17
+ exists: boolean;
18
+ parsed: boolean;
19
+ error?: string;
20
+ }
21
+ export interface CollectedMcpServer {
22
+ server: McpServerConfig;
23
+ sources: string[];
24
+ serversBySource: Map<string, McpServerConfig>;
25
+ }
26
+ export declare const RAILWAY_REMOTE_MCP_URL = "https://mcp.railway.com";
27
+ export declare function loadConfig(filePath: string): Promise<ConfigSource>;
28
+ export declare function resolveAllConfigs(): Promise<ConfigSource[]>;
29
+ export declare function collectServers(sources: ConfigSource[]): Map<string, CollectedMcpServer>;
30
+ export declare function selectEffectiveServer(info: CollectedMcpServer, activePathOrder: string[]): McpServerConfig;
31
+ export declare function basenameOfCommand(command: string): string;
32
+ export declare function isNpmLauncherCommand(command: string | undefined): boolean;
33
+ export declare function validateRemoteMcpUrl(url: string): {
34
+ ok: true;
35
+ } | {
36
+ ok: false;
37
+ message: string;
38
+ };
39
+ export declare function sanitizeMcpUrlForDisplay(url: string): string;
40
+ export declare function serverTargetText(server: McpServerConfig): string;
41
+ export declare function isRailwayMcpServer(name: string, server: McpServerConfig): boolean;
42
+ export declare function isSupabaseMcpServer(name: string, server: McpServerConfig): boolean;
43
+ export declare function formatArgsForDisplay(args: string[]): string;
44
+ export declare function sanitizeMcpServerForProject(server: McpServerConfig): McpServerConfig;
45
+ export declare function sanitizeMcpArgsForProject(args: string[]): string[];
46
+ export declare function isHttpUrl(value: string): boolean;
47
+ export declare function isSecretEnvName(name: string): boolean;