open-multi-agent-kit 0.78.5 → 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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +64 -0
- package/MATURITY.md +2 -2
- package/README.md +20 -5
- package/dist/brand/night-city.theme.json +80 -0
- package/dist/brand/palette.d.ts +73 -177
- package/dist/brand/palette.js +115 -39
- package/dist/brand/rust-forge.theme.json +216 -0
- package/dist/brand/theme-compiled.d.ts +61 -0
- package/dist/brand/theme-compiled.js +241 -0
- package/dist/brand/theme.js +50 -37
- package/dist/cli/release-promotion-gate.d.ts +8 -1
- package/dist/cli/release-promotion-gate.js +23 -11
- package/dist/cli/root.d.ts +1 -0
- package/dist/cli/root.js +2 -1
- package/dist/cli/theme/index.d.ts +11 -2
- package/dist/cli/theme/index.js +6 -1
- package/dist/cli/theme/oklab-quantize.d.ts +29 -0
- package/dist/cli/theme/oklab-quantize.js +105 -0
- package/dist/cli/theme/render-table.d.ts +59 -0
- package/dist/cli/theme/render-table.js +112 -0
- package/dist/cli/theme/status-frame.d.ts +10 -0
- package/dist/cli/theme/status-frame.js +22 -0
- package/dist/cli/theme/terminal-capability.d.ts +6 -1
- package/dist/cli/theme/terminal-capability.js +20 -4
- package/dist/cli/theme/theme-doc.d.ts +22 -0
- package/dist/cli/theme/theme-doc.js +97 -0
- package/dist/cli/theme/theme-registry.js +39 -18
- package/dist/cli/theme/tier-explain.d.ts +21 -0
- package/dist/cli/theme/tier-explain.js +60 -0
- package/dist/cli/ui/green-rain-renderer.js +3 -2
- package/dist/cli/ui/neon-grid-renderer.js +4 -3
- package/dist/cli/ui/rust-forge-renderer.js +17 -3
- package/dist/cli/ui/system24-renderer.d.ts +18 -1
- package/dist/cli/ui/system24-renderer.js +39 -16
- package/dist/cli/v2/chat-repl.js +3 -1
- package/dist/cli/v2/cli-v2-skeleton.d.ts +6 -0
- package/dist/cli/v2/cli-v2-skeleton.js +121 -9
- package/dist/cli/v2/interactive-prompt.d.ts +1 -0
- package/dist/cli/v2/interactive-prompt.js +6 -2
- package/dist/commands/chat/core.js +2 -2
- package/dist/commands/chat/runtime.js +2 -0
- package/dist/commands/chat/utils.js +12 -4
- package/dist/commands/cockpit/render.js +102 -64
- package/dist/commands/cockpit/utils.d.ts +2 -0
- package/dist/commands/design.js +18 -16
- package/dist/commands/doctor/checks.d.ts +0 -1
- package/dist/commands/doctor/checks.js +1 -36
- package/dist/commands/doctor/report.js +0 -11
- package/dist/commands/init/content.d.ts +1 -1
- package/dist/commands/init/content.js +3 -11
- package/dist/commands/init.js +2 -2
- package/dist/commands/mcp/config.d.ts +32 -0
- package/dist/commands/mcp/config.js +264 -0
- package/dist/commands/mcp/doctor-fix.d.ts +12 -0
- package/dist/commands/mcp/doctor-fix.js +261 -0
- package/dist/commands/mcp/doctor.d.ts +55 -0
- package/dist/commands/mcp/doctor.js +401 -0
- package/dist/commands/mcp/list.d.ts +1 -0
- package/dist/commands/mcp/list.js +56 -0
- package/dist/commands/mcp/shared.d.ts +47 -0
- package/dist/commands/mcp/shared.js +192 -0
- package/dist/commands/mcp/test.d.ts +5 -0
- package/dist/commands/mcp/test.js +457 -0
- package/dist/commands/mcp.d.ts +8 -104
- package/dist/commands/mcp.js +5 -1603
- package/dist/contracts/proof.d.ts +1 -1
- package/dist/goal/control-loop.d.ts +15 -0
- package/dist/goal/control-loop.js +39 -5
- package/dist/hud/render.d.ts +2 -0
- package/dist/hud/render.js +7 -1
- package/dist/mcp/secret-scanner.d.ts +6 -0
- package/dist/mcp/secret-scanner.js +44 -11
- package/dist/memory/graph-delta-log.d.ts +137 -0
- package/dist/memory/graph-delta-log.js +650 -0
- package/dist/memory/graph-viewer.js +13 -32
- package/dist/memory/local-graph-memory-store.d.ts +36 -3
- package/dist/memory/local-graph-memory-store.js +255 -10
- package/dist/orchestration/log-streamer.js +13 -8
- package/dist/orchestration/routing.d.ts +4 -0
- package/dist/orchestration/routing.js +28 -3
- package/dist/orchestration/state-persister.js +8 -1
- package/dist/providers/anthropic-messages-runner.d.ts +17 -0
- package/dist/providers/anthropic-messages-runner.js +197 -0
- package/dist/providers/model-registry.d.ts +1 -1
- package/dist/providers/model-registry.js +37 -3
- package/dist/providers/provider-runtime.js +27 -11
- package/dist/providers/thinking-levels.js +4 -1
- package/dist/providers/types.d.ts +3 -3
- package/dist/runtime/contracts/weakness-remediation.d.ts +11 -1
- package/dist/runtime/contracts/weakness-remediation.js +9 -1
- package/dist/runtime/router-v2-scoring.d.ts +8 -3
- package/dist/runtime/router-v2-scoring.js +27 -22
- package/dist/runtime/runtime-router.d.ts +5 -0
- package/dist/runtime/runtime-router.js +40 -8
- package/dist/runtime/sandbox-profile.d.ts +29 -0
- package/dist/runtime/sandbox-profile.js +101 -0
- package/dist/runtime/slash-commands.js +300 -2
- package/dist/runtime/tool-dispatch-contracts.d.ts +15 -0
- package/dist/runtime/tool-dispatch-contracts.js +12 -0
- package/dist/runtime/weights-config.d.ts +90 -0
- package/dist/runtime/weights-config.js +249 -0
- package/dist/schema/envelope.schema.d.ts +18 -18
- package/dist/schema/evidence.schema.d.ts +2 -2
- package/dist/schema/proof-bundle.schema.d.ts +6 -6
- package/dist/schema/proof-bundle.schema.js +0 -1
- package/dist/schema/provider.schema.d.ts +2 -2
- package/dist/schema/run-manifest.schema.d.ts +16 -16
- package/dist/theme/ansi.js +9 -2
- package/dist/theme/extended-palette.d.ts +85 -0
- package/dist/theme/extended-palette.js +108 -0
- package/dist/theme/layout.js +29 -8
- package/dist/theme/metrics.js +5 -5
- package/dist/theme/parallel.js +25 -15
- package/dist/ui/omk-sigil.js +5 -14
- package/dist/ui/omk-working-sweep.js +5 -15
- package/dist/util/chat-agent-mode.d.ts +11 -0
- package/dist/util/chat-agent-mode.js +41 -8
- package/dist/util/chat-cockpit.d.ts +9 -0
- package/dist/util/chat-cockpit.js +77 -20
- package/dist/util/fs/core.d.ts +5 -0
- package/dist/util/fs/core.js +35 -0
- package/dist/util/fs/internal.d.ts +14 -0
- package/dist/util/fs/internal.js +169 -0
- package/dist/util/fs/kimi-sync.d.ts +41 -0
- package/dist/util/fs/kimi-sync.js +495 -0
- package/dist/util/fs/logo.d.ts +2 -0
- package/dist/util/fs/logo.js +78 -0
- package/dist/util/fs/manifest.d.ts +6 -0
- package/dist/util/fs/manifest.js +40 -0
- package/dist/util/fs/mcp-diagnose.d.ts +15 -0
- package/dist/util/fs/mcp-diagnose.js +204 -0
- package/dist/util/fs/mcp-runtime-config.d.ts +23 -0
- package/dist/util/fs/mcp-runtime-config.js +412 -0
- package/dist/util/fs/paths.d.ts +13 -0
- package/dist/util/fs/paths.js +82 -0
- package/dist/util/fs/preflight.d.ts +27 -0
- package/dist/util/fs/preflight.js +271 -0
- package/dist/util/fs.d.ts +8 -128
- package/dist/util/fs.js +8 -1757
- package/dist/util/terminal-layout.js +5 -1
- package/dist/util/version.js +2 -1
- package/docs/2026-06-10/critical-issues.md +20 -0
- package/docs/2026-06-10/improvements.md +14 -0
- package/docs/2026-06-10/init-checklist.md +25 -0
- package/docs/2026-06-10/plan.md +20 -0
- package/docs/2026-06-11/critical-issues.md +20 -0
- package/docs/2026-06-11/improvements.md +14 -0
- package/docs/2026-06-11/init-checklist.md +25 -0
- package/docs/2026-06-11/plan.md +20 -0
- package/docs/ARCHITECTURE_ANALYSIS_CODEGRAPH.md +223 -0
- package/docs/adr/0001-no-native-rust-lane.md +52 -0
- package/docs/codex-oauth-setup.md +14 -0
- package/docs/decisions/2026-06-10-git-history-bloat.md +61 -0
- package/docs/decisions/ADR-theme-dark-only-assets.md +43 -0
- package/docs/getting-started.md +1 -1
- package/docs/headroom-omk-final-summary.md +188 -0
- package/docs/headroom-omk-integration.md +394 -0
- package/docs/headroom-omk-setup-guide.md +422 -0
- package/docs/headroom-omk-usage-examples.md +371 -0
- package/docs/provider-maturity.md +1 -1
- package/docs/versioning.md +3 -3
- package/package.json +10 -10
- package/readmeasset/.npmignore +2 -0
- package/readmeasset/ASSET_INDEX.md +7 -5
- package/readmeasset/ASSET_PROVENANCE.md +21 -5
- package/readmeasset/omk-control-surfaces.svg +55 -0
- package/readmeasset/omk-core-loop.svg +1 -0
- package/readmeasset/omk-logo-mark.svg +1 -0
- package/readmeasset/omk-release-assertions.svg +51 -0
- package/dist/native/linux-x64/omk-safety +0 -0
- package/dist/util/native-safety.d.ts +0 -28
- package/dist/util/native-safety.js +0 -118
- package/templates/skills/agents/omk-adaptorch-orchestration-review/SKILL.md +0 -52
- package/templates/skills/kimi/mcp-install/SKILL.md +0 -66
- package/templates/skills/kimi/omk-adaptorch-orchestration-review/SKILL.md +0 -52
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { getProjectRoot, pathExists, getUserHome, } from "../../util/fs.js";
|
|
4
|
+
import { maskSensitiveText } from "../../util/secret-mask.js";
|
|
5
|
+
export const RAILWAY_REMOTE_MCP_URL = "https://mcp.railway.com";
|
|
6
|
+
export async function loadConfig(filePath) {
|
|
7
|
+
if (!(await pathExists(filePath))) {
|
|
8
|
+
return { path: filePath, config: {}, exists: false, parsed: true };
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const content = await readFile(filePath, "utf-8");
|
|
12
|
+
const config = JSON.parse(content);
|
|
13
|
+
return { path: filePath, config, exists: true, parsed: true };
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
17
|
+
return { path: filePath, config: {}, exists: true, parsed: false, error: `Invalid JSON: ${message}` };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function resolveAllConfigs() {
|
|
21
|
+
const root = getProjectRoot();
|
|
22
|
+
const paths = [
|
|
23
|
+
join(root, ".omk", "mcp.json"),
|
|
24
|
+
join(root, ".kimi", "mcp.json"),
|
|
25
|
+
join(getUserHome(), ".kimi", "mcp.json"),
|
|
26
|
+
join(getUserHome(), ".omk", "mcp.json"),
|
|
27
|
+
];
|
|
28
|
+
const results = [];
|
|
29
|
+
for (const p of paths) {
|
|
30
|
+
results.push(await loadConfig(p));
|
|
31
|
+
}
|
|
32
|
+
return results;
|
|
33
|
+
}
|
|
34
|
+
export function collectServers(sources) {
|
|
35
|
+
const map = new Map();
|
|
36
|
+
for (const src of sources) {
|
|
37
|
+
if (!src.parsed || !src.config.mcpServers)
|
|
38
|
+
continue;
|
|
39
|
+
for (const [name, server] of Object.entries(src.config.mcpServers)) {
|
|
40
|
+
const existing = map.get(name);
|
|
41
|
+
if (existing) {
|
|
42
|
+
existing.sources.push(src.path);
|
|
43
|
+
existing.serversBySource.set(src.path, server);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
map.set(name, { server, sources: [src.path], serversBySource: new Map([[src.path, server]]) });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return map;
|
|
51
|
+
}
|
|
52
|
+
export function selectEffectiveServer(info, activePathOrder) {
|
|
53
|
+
for (let index = activePathOrder.length - 1; index >= 0; index--) {
|
|
54
|
+
const server = info.serversBySource.get(activePathOrder[index]);
|
|
55
|
+
if (server)
|
|
56
|
+
return server;
|
|
57
|
+
}
|
|
58
|
+
return info.server;
|
|
59
|
+
}
|
|
60
|
+
export function basenameOfCommand(command) {
|
|
61
|
+
return command.replace(/\\/g, "/").split("/").pop()?.toLowerCase() ?? command.toLowerCase();
|
|
62
|
+
}
|
|
63
|
+
export function isNpmLauncherCommand(command) {
|
|
64
|
+
if (!command)
|
|
65
|
+
return false;
|
|
66
|
+
return ["npm", "npx", "npm.cmd", "npx.cmd", "npm.exe", "npx.exe"].includes(basenameOfCommand(command));
|
|
67
|
+
}
|
|
68
|
+
export function validateRemoteMcpUrl(url) {
|
|
69
|
+
try {
|
|
70
|
+
const parsed = new URL(url);
|
|
71
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
72
|
+
return { ok: false, message: "remote MCP URL must use http or https" };
|
|
73
|
+
}
|
|
74
|
+
return { ok: true };
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
78
|
+
return { ok: false, message };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const SECRET_QUERY_KEYS = /^(token|api[-_]?key|key|secret|password|auth|credential|session|bearer|access[-_]?token|refresh[-_]?token|client[-_]?secret|x[-_]?auth[-_]?token|signature|sig|jwt|private[-_]?key|pat|dsn)$/i;
|
|
82
|
+
export function sanitizeMcpUrlForDisplay(url) {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = new URL(url);
|
|
85
|
+
let changed = false;
|
|
86
|
+
if (parsed.username || parsed.password) {
|
|
87
|
+
parsed.username = "***";
|
|
88
|
+
parsed.password = "";
|
|
89
|
+
changed = true;
|
|
90
|
+
}
|
|
91
|
+
for (const [key] of parsed.searchParams) {
|
|
92
|
+
if (SECRET_QUERY_KEYS.test(key)) {
|
|
93
|
+
parsed.searchParams.set(key, "***");
|
|
94
|
+
changed = true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (parsed.hash) {
|
|
98
|
+
parsed.hash = "#***";
|
|
99
|
+
changed = true;
|
|
100
|
+
}
|
|
101
|
+
return changed ? parsed.toString() : url;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return url;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function serverTargetText(server) {
|
|
108
|
+
return [server.url, server.command, ...(server.args ?? [])].filter(Boolean).join(" ");
|
|
109
|
+
}
|
|
110
|
+
export function isRailwayMcpServer(name, server) {
|
|
111
|
+
return /railway/i.test(name) || /railway/i.test(serverTargetText(server));
|
|
112
|
+
}
|
|
113
|
+
export function isSupabaseMcpServer(name, server) {
|
|
114
|
+
return /supabase/i.test(name) || /supabase/i.test(serverTargetText(server));
|
|
115
|
+
}
|
|
116
|
+
export function formatArgsForDisplay(args) {
|
|
117
|
+
return sanitizeMcpArgsForProject(args).join(" ");
|
|
118
|
+
}
|
|
119
|
+
export function sanitizeMcpServerForProject(server) {
|
|
120
|
+
const cleaned = JSON.parse(JSON.stringify(server));
|
|
121
|
+
if (typeof cleaned.url === "string") {
|
|
122
|
+
cleaned.url = sanitizeMcpUrlForDisplay(cleaned.url);
|
|
123
|
+
}
|
|
124
|
+
if (typeof cleaned.command === "string") {
|
|
125
|
+
cleaned.command = maskSensitiveText(cleaned.command);
|
|
126
|
+
}
|
|
127
|
+
if (Array.isArray(cleaned.args)) {
|
|
128
|
+
cleaned.args = sanitizeMcpArgsForProject(cleaned.args);
|
|
129
|
+
}
|
|
130
|
+
if (cleaned.env && typeof cleaned.env === "object") {
|
|
131
|
+
const env = {};
|
|
132
|
+
for (const [key, value] of Object.entries(cleaned.env)) {
|
|
133
|
+
env[key] = isSecretEnvName(key) ? `\${${key}}` : value;
|
|
134
|
+
}
|
|
135
|
+
cleaned.env = env;
|
|
136
|
+
}
|
|
137
|
+
if (cleaned.headers && typeof cleaned.headers === "object") {
|
|
138
|
+
const headers = {};
|
|
139
|
+
for (const [key, value] of Object.entries(cleaned.headers)) {
|
|
140
|
+
headers[key] = isSecretHeaderName(key) ? "[REDACTED]" : value;
|
|
141
|
+
}
|
|
142
|
+
cleaned.headers = headers;
|
|
143
|
+
}
|
|
144
|
+
if (cleaned.http_headers && typeof cleaned.http_headers === "object") {
|
|
145
|
+
const headers = {};
|
|
146
|
+
for (const [key, value] of Object.entries(cleaned.http_headers)) {
|
|
147
|
+
headers[key] = isSecretHeaderName(key) ? "[REDACTED]" : value;
|
|
148
|
+
}
|
|
149
|
+
cleaned.http_headers = headers;
|
|
150
|
+
}
|
|
151
|
+
const nestedConfig = cleaned.config;
|
|
152
|
+
if (nestedConfig && typeof nestedConfig === "object" && !Array.isArray(nestedConfig)) {
|
|
153
|
+
const config = nestedConfig;
|
|
154
|
+
if (config.env && typeof config.env === "object" && !Array.isArray(config.env)) {
|
|
155
|
+
const env = {};
|
|
156
|
+
for (const [key, value] of Object.entries(config.env)) {
|
|
157
|
+
env[key] = isSecretEnvName(key) ? `\${${key}}` : value;
|
|
158
|
+
}
|
|
159
|
+
config.env = env;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return cleaned;
|
|
163
|
+
}
|
|
164
|
+
export function sanitizeMcpArgsForProject(args) {
|
|
165
|
+
return args.map((arg, index) => {
|
|
166
|
+
if (typeof arg !== "string")
|
|
167
|
+
return arg;
|
|
168
|
+
const previous = index > 0 ? args[index - 1] : "";
|
|
169
|
+
if (typeof previous === "string" && isSplitSecretCliOption(previous))
|
|
170
|
+
return "[REDACTED]";
|
|
171
|
+
if (isSecretCliOption(arg)) {
|
|
172
|
+
const eq = arg.indexOf("=");
|
|
173
|
+
return eq > 0 ? `${arg.slice(0, eq + 1)}[REDACTED]` : arg;
|
|
174
|
+
}
|
|
175
|
+
return maskSensitiveText(arg);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function isSplitSecretCliOption(value) {
|
|
179
|
+
return value.indexOf("=") === -1 && isSecretCliOption(value);
|
|
180
|
+
}
|
|
181
|
+
function isSecretCliOption(value) {
|
|
182
|
+
return /^--?(?:api[-_]?key|api[-_]?token|token|key|secret|password|passwd|client[-_]?secret|x[-_]?auth[-_]?token|auth|authorization|credential|cookie|session|jwt|private[-_]?key)(?:=.*)?$/i.test(value);
|
|
183
|
+
}
|
|
184
|
+
export function isHttpUrl(value) {
|
|
185
|
+
return /^https?:\/\//i.test(value);
|
|
186
|
+
}
|
|
187
|
+
export function isSecretEnvName(name) {
|
|
188
|
+
return /(?:SECRET|TOKEN|KEY|PASSWORD|PASSWD|CREDENTIAL|AUTH|COOKIE|SESSION|PRIVATE|DATABASE_URL|DATABASE_URI|CONNECTION_STRING|CONNECTION_URI|MONGO(?:DB)?_URI|REDIS_URL|POSTGRES(?:QL)?_URL|MYSQL_URL|DSN|PAT|JWT|BEARER|OAUTH)/i.test(name);
|
|
189
|
+
}
|
|
190
|
+
function isSecretHeaderName(name) {
|
|
191
|
+
return /authorization|x-api-key|api-key|cookie|set-cookie|x-auth-token|token|secret|signature/i.test(name);
|
|
192
|
+
}
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { runShell, which } from "../../util/shell.js";
|
|
2
|
+
import { collectMcpConfigs, getProjectRoot, preflightRuntimeMcpServers, resolveRuntimeMcpPreflightOptions, } from "../../util/fs.js";
|
|
3
|
+
import { getOmkResourceSettings } from "../../util/resource-profile.js";
|
|
4
|
+
import { style, header, status, label } from "../../util/theme.js";
|
|
5
|
+
import { maskSensitiveText } from "../../util/secret-mask.js";
|
|
6
|
+
import { buildSubprocessEnv } from "../../mcp/transports/stdio.js";
|
|
7
|
+
import { McpClientSession } from "../../mcp/client.js";
|
|
8
|
+
import { collectServers, formatArgsForDisplay, resolveAllConfigs, sanitizeMcpUrlForDisplay, selectEffectiveServer, serverTargetText, validateRemoteMcpUrl, } from "./shared.js";
|
|
9
|
+
const JSON_RPC_INTERNAL_ERROR_CODE = -32603;
|
|
10
|
+
export async function mcpTestCommand(serverName) {
|
|
11
|
+
const sources = await resolveAllConfigs();
|
|
12
|
+
const servers = collectServers(sources);
|
|
13
|
+
const info = servers.get(serverName);
|
|
14
|
+
if (!info) {
|
|
15
|
+
console.error(status.error(`Server not found: ${serverName}`));
|
|
16
|
+
console.error(style.gray("Run `omk mcp list` to see available servers."));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const resources = await getOmkResourceSettings();
|
|
20
|
+
const activePathOrder = await collectMcpConfigs(resources.mcpScope);
|
|
21
|
+
const activeSources = info.sources.filter((source) => activePathOrder.includes(source));
|
|
22
|
+
if (activeSources.length === 0) {
|
|
23
|
+
console.error(status.error(`Server ${serverName} is not active in MCP scope "${resources.mcpScope}".`));
|
|
24
|
+
console.error(style.gray(`Defined in: ${info.sources.join(", ")}`));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const server = selectEffectiveServer(info, activePathOrder);
|
|
28
|
+
if (!server.url && !server.command) {
|
|
29
|
+
console.error(status.error(`Server ${serverName} has no command`));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
console.log(header(`MCP Test: ${serverName}`));
|
|
33
|
+
if (server.url) {
|
|
34
|
+
console.log(label("URL", sanitizeMcpUrlForDisplay(server.url)));
|
|
35
|
+
await testRemoteMcpServer(serverName, server);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const command = server.command;
|
|
39
|
+
if (!command) {
|
|
40
|
+
console.error(status.error(`Server ${serverName} has no command`));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
console.log(label("Command", maskSensitiveText(command)));
|
|
44
|
+
if (server.args)
|
|
45
|
+
console.log(label("Args", formatArgsForDisplay(server.args)));
|
|
46
|
+
console.log("");
|
|
47
|
+
const testEnv = buildSubprocessEnv(process.env, server.env ?? {});
|
|
48
|
+
// Test 1: executable exists
|
|
49
|
+
const resolved = await which(command);
|
|
50
|
+
if (resolved.failed) {
|
|
51
|
+
console.error(status.error(`Executable not found: ${maskSensitiveText(command)}`));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
console.log(status.ok(`Executable: ${maskSensitiveText(resolved.stdout.trim())}`));
|
|
55
|
+
// Test 2: try to run with a 5s timeout as a basic smoke test
|
|
56
|
+
const smokeArgs = [...(server.args ?? [])];
|
|
57
|
+
console.log(style.gray("Smoke test: starting process (5s timeout)..."));
|
|
58
|
+
const smokeResult = await runShell(command, smokeArgs, {
|
|
59
|
+
cwd: getProjectRoot(),
|
|
60
|
+
timeout: 5000,
|
|
61
|
+
env: testEnv,
|
|
62
|
+
});
|
|
63
|
+
const smokePollution = findNonJsonStdoutLines(smokeResult.stdout);
|
|
64
|
+
if (smokePollution.length > 0) {
|
|
65
|
+
console.error(status.error(`Server wrote non-JSON text to stdout during startup: ${maskSensitiveText(smokePollution[0])}`));
|
|
66
|
+
console.error(style.gray("MCP stdio servers must write only JSON-RPC frames to stdout. Move logs to stderr, remove this server, or configure it as a remote URL if it is an HTTP MCP server."));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
if (!smokeResult.failed) {
|
|
70
|
+
console.log(status.ok(`Process exited with code ${smokeResult.exitCode}`));
|
|
71
|
+
}
|
|
72
|
+
else if (smokeResult.stderr.includes("timeout") || smokeResult.stderr.includes("ETIMEDOUT")) {
|
|
73
|
+
console.log(style.skin("Process is still running after 5s (expected for stdio MCP servers)"));
|
|
74
|
+
}
|
|
75
|
+
else if (!smokeResult.stderr.trim()) {
|
|
76
|
+
console.log(status.ok(`Process started and exited without stderr (code ${smokeResult.exitCode})`));
|
|
77
|
+
}
|
|
78
|
+
else if (smokeResult.stderr.includes("ENOENT")) {
|
|
79
|
+
console.error(status.error(`Failed to start: ${maskSensitiveText(smokeResult.stderr)}`));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.log(style.gray(`Process exited with error (may be OK for stdio servers): ${maskSensitiveText(smokeResult.stderr)}`));
|
|
84
|
+
}
|
|
85
|
+
// Test 3: JSON-RPC initialize handshake
|
|
86
|
+
console.log("");
|
|
87
|
+
console.log(style.gray("JSON-RPC handshake test..."));
|
|
88
|
+
const initializePayload = JSON.stringify({
|
|
89
|
+
jsonrpc: "2.0",
|
|
90
|
+
id: 1,
|
|
91
|
+
method: "initialize",
|
|
92
|
+
params: {
|
|
93
|
+
protocolVersion: "2024-11-05",
|
|
94
|
+
capabilities: {},
|
|
95
|
+
clientInfo: {
|
|
96
|
+
name: "omk-mcp-test",
|
|
97
|
+
version: "0.0.0",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
}) + "\n";
|
|
101
|
+
const handshakeTimeoutMs = Math.max(1000, Math.min((server.startup_timeout_sec ?? 20) * 1000, 20_000));
|
|
102
|
+
const handshakeResult = await runShell(command, smokeArgs, {
|
|
103
|
+
cwd: getProjectRoot(),
|
|
104
|
+
timeout: handshakeTimeoutMs,
|
|
105
|
+
env: testEnv,
|
|
106
|
+
input: initializePayload,
|
|
107
|
+
});
|
|
108
|
+
const stdoutPollution = findNonJsonStdoutLines(handshakeResult.stdout);
|
|
109
|
+
if (stdoutPollution.length > 0) {
|
|
110
|
+
console.error(status.error(`Server wrote non-JSON text to stdout before/during JSON-RPC: ${maskSensitiveText(stdoutPollution[0])}`));
|
|
111
|
+
console.error(style.gray("MCP stdio servers must write only JSON-RPC frames to stdout. Move logs to stderr, remove this server, or configure it as a remote URL if it is an HTTP MCP server."));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
const handshakeResponses = parseJsonRpcResponses(handshakeResult.stdout);
|
|
115
|
+
const internalHandshakeError = findInternalJsonRpcError(handshakeResponses);
|
|
116
|
+
if (internalHandshakeError) {
|
|
117
|
+
console.error(status.error(`JSON-RPC initialize returned Internal error: ${maskSensitiveText(internalHandshakeError.error?.message ?? "unknown")}`));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const initializeResponse = findJsonRpcResponseById(handshakeResponses, 1);
|
|
121
|
+
const serverInfo = extractInitializeServerInfo(initializeResponse);
|
|
122
|
+
if (serverInfo) {
|
|
123
|
+
console.log(style.mint("JSON-RPC initialize succeeded"));
|
|
124
|
+
}
|
|
125
|
+
else if (!handshakeResult.failed) {
|
|
126
|
+
const detail = initializeResponse ? "initialize response missing serverInfo" : `server exited without initialize response (exit code ${handshakeResult.exitCode})`;
|
|
127
|
+
console.error(status.error(`JSON-RPC initialize failed: ${detail}`));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
else if (/timed?\s*out|timeout|ETIMEDOUT/i.test(handshakeResult.stderr)) {
|
|
131
|
+
console.error(status.error("JSON-RPC initialize timed out before serverInfo was received"));
|
|
132
|
+
if (handshakeResult.stderr.trim())
|
|
133
|
+
console.error(style.gray(maskSensitiveText(handshakeResult.stderr.trim())));
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.error(status.error(`JSON-RPC initialize failed: ${maskSensitiveText(handshakeResult.stderr || "unknown error")}`));
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
if (shouldRunOmkProjectProbe(serverName, server)) {
|
|
141
|
+
await runOmkProjectToolProbe(command, smokeArgs, server.env);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
export async function mcpPrewarmCommand(serverName, options = {}) {
|
|
145
|
+
const isCheck = options.label === "check";
|
|
146
|
+
if (options.all) {
|
|
147
|
+
console.log(header(isCheck ? "MCP Check All" : "MCP Preflight Check All"));
|
|
148
|
+
console.log(style.gray("Bounded npm-family dependency-resolution check for active MCP servers; this does not force-install packages."));
|
|
149
|
+
const resources = await getOmkResourceSettings();
|
|
150
|
+
const activePathOrder = await collectMcpConfigs(resources.mcpScope);
|
|
151
|
+
const activePaths = new Set(activePathOrder);
|
|
152
|
+
const sources = await resolveAllConfigs();
|
|
153
|
+
const servers = collectServers(sources);
|
|
154
|
+
if (servers.size === 0) {
|
|
155
|
+
console.log(style.gray("No MCP servers configured."));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const activeServers = {};
|
|
159
|
+
const inactive = new Set();
|
|
160
|
+
for (const [name, info] of servers) {
|
|
161
|
+
const activeSources = info.sources.filter((source) => activePaths.has(source));
|
|
162
|
+
if (activeSources.length === 0) {
|
|
163
|
+
inactive.add(name);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
activeServers[name] = selectEffectiveServer(info, activePathOrder);
|
|
167
|
+
}
|
|
168
|
+
const preflightOptions = resolveRuntimeMcpPreflightOptions();
|
|
169
|
+
const preflight = await preflightRuntimeMcpServers(activeServers, preflightOptions);
|
|
170
|
+
const entriesByName = new Map(preflight.entries.map((entry) => [entry.name, entry]));
|
|
171
|
+
let okCount = 0;
|
|
172
|
+
let failCount = 0;
|
|
173
|
+
let checkedCount = 0;
|
|
174
|
+
let skipCount = inactive.size;
|
|
175
|
+
for (const [name] of servers) {
|
|
176
|
+
if (inactive.has(name)) {
|
|
177
|
+
console.log(`${style.gray("-")} ${style.gray(name)} ${style.gray("[inactive]")}`);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
checkedCount++;
|
|
181
|
+
const entry = entriesByName.get(name);
|
|
182
|
+
if (!entry || entry.status === "skipped") {
|
|
183
|
+
skipCount++;
|
|
184
|
+
const reason = entry?.reason === "no-package-spec" ? "no package spec" : "not npm-family";
|
|
185
|
+
console.log(`${style.gray("-")} ${name} ${style.gray(`[skipped: ${reason}]`)}`);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (entry.status === "ok") {
|
|
189
|
+
okCount++;
|
|
190
|
+
const packageNote = entry.packageSpec ? ` (${entry.packageSpec})` : "";
|
|
191
|
+
console.log(`${style.mint("✓")} ${name}${style.gray(packageNote)}`);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
failCount++;
|
|
195
|
+
console.log(`${style.pink("✗")} ${name}: ${maskSensitiveText(entry.detail ?? "failed")}`);
|
|
196
|
+
}
|
|
197
|
+
console.log("");
|
|
198
|
+
if (failCount === 0) {
|
|
199
|
+
console.log(status.ok(`Checked ${checkedCount} server(s); ${skipCount} skipped`));
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
process.exitCode = 1;
|
|
203
|
+
console.log(status.error(`${failCount} failure(s), ${okCount} ok, ${skipCount} skipped`));
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (!serverName) {
|
|
208
|
+
console.error(status.error("Provide a server name or use --all"));
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
console.log(header(`${isCheck ? "MCP Check" : "MCP Prewarm/Check"}: ${serverName}`));
|
|
212
|
+
console.log(style.gray("Runs the MCP startup probe outside chat; package-manager caches may warm as a side effect."));
|
|
213
|
+
console.log(style.gray("For a zero-global-noise session, run chat/goal/parallel with `--mcp-scope project` or `--mcp-scope none`."));
|
|
214
|
+
await mcpTestCommand(serverName);
|
|
215
|
+
}
|
|
216
|
+
function shouldRunOmkProjectProbe(serverName, server) {
|
|
217
|
+
const target = serverTargetText(server);
|
|
218
|
+
return serverName === "omk-project" || /\bomk-project\b/.test(target);
|
|
219
|
+
}
|
|
220
|
+
async function runOmkProjectToolProbe(command, args, env) {
|
|
221
|
+
console.log("");
|
|
222
|
+
console.log(style.gray("JSON-RPC OMK tools/call id 3 probe..."));
|
|
223
|
+
const payload = [
|
|
224
|
+
{
|
|
225
|
+
jsonrpc: "2.0",
|
|
226
|
+
id: 1,
|
|
227
|
+
method: "initialize",
|
|
228
|
+
params: {
|
|
229
|
+
protocolVersion: "2024-11-05",
|
|
230
|
+
capabilities: {},
|
|
231
|
+
clientInfo: { name: "omk-mcp-test", version: "0.0.0" },
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
jsonrpc: "2.0",
|
|
236
|
+
id: 2,
|
|
237
|
+
method: "tools/list",
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
jsonrpc: "2.0",
|
|
241
|
+
id: 3,
|
|
242
|
+
method: "tools/call",
|
|
243
|
+
params: {
|
|
244
|
+
name: "omk_goal_show",
|
|
245
|
+
arguments: { goalId: "missing-goal" },
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
].map((message) => JSON.stringify(message)).join("\n") + "\n";
|
|
249
|
+
const probeResult = await runShell(command, args, {
|
|
250
|
+
cwd: getProjectRoot(),
|
|
251
|
+
timeout: 20000,
|
|
252
|
+
env: buildSubprocessEnv(process.env, env ?? {}),
|
|
253
|
+
input: payload,
|
|
254
|
+
});
|
|
255
|
+
const responses = parseJsonRpcResponses(probeResult.stdout);
|
|
256
|
+
const internalError = findInternalJsonRpcError(responses);
|
|
257
|
+
if (internalError) {
|
|
258
|
+
console.error(status.error(`JSON-RPC id ${String(internalError.id ?? "?")} returned Internal error: ${internalError.error?.message ?? "unknown"}`));
|
|
259
|
+
if (probeResult.stderr.trim())
|
|
260
|
+
console.error(style.gray(maskSensitiveText(probeResult.stderr.trim())));
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
const toolResponse = responses.find((response) => response.id === 3);
|
|
264
|
+
if (!toolResponse) {
|
|
265
|
+
const timeoutHint = probeResult.stderr.includes("timeout") || probeResult.stderr.includes("ETIMEDOUT")
|
|
266
|
+
? " before the probe timeout"
|
|
267
|
+
: "";
|
|
268
|
+
console.error(status.error(`JSON-RPC tools/call id 3 did not return a response${timeoutHint}`));
|
|
269
|
+
if (probeResult.stderr.trim())
|
|
270
|
+
console.error(style.gray(maskSensitiveText(probeResult.stderr.trim())));
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
if (toolResponse.error) {
|
|
274
|
+
console.error(status.error(`JSON-RPC id 3 returned error ${toolResponse.error.code ?? "unknown"}: ${toolResponse.error.message ?? "unknown"}`));
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
const toolText = stringifyToolResultContent(toolResponse.result);
|
|
278
|
+
if (!isRecord(toolResponse.result) || toolResponse.result.isError !== true || !toolText.includes("OMK tool-level failure")) {
|
|
279
|
+
console.error(status.error("JSON-RPC id 3 did not return an OMK tool-level error result"));
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
console.log(status.ok("JSON-RPC tools/call id 3 returned OMK tool-level error without -32603"));
|
|
283
|
+
}
|
|
284
|
+
function findNonJsonStdoutLines(stdout) {
|
|
285
|
+
const invalidLines = [];
|
|
286
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
287
|
+
const trimmed = line.trim();
|
|
288
|
+
if (!trimmed)
|
|
289
|
+
continue;
|
|
290
|
+
try {
|
|
291
|
+
const parsed = JSON.parse(trimmed);
|
|
292
|
+
if (!isJsonRpcFrame(parsed))
|
|
293
|
+
invalidLines.push(trimmed);
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
invalidLines.push(trimmed);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return invalidLines;
|
|
300
|
+
}
|
|
301
|
+
function isJsonRpcFrame(value) {
|
|
302
|
+
if (!isRecord(value) || value.jsonrpc !== "2.0")
|
|
303
|
+
return false;
|
|
304
|
+
return "id" in value || "method" in value || "result" in value || "error" in value;
|
|
305
|
+
}
|
|
306
|
+
function parseJsonRpcResponses(stdout) {
|
|
307
|
+
const responses = [];
|
|
308
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
309
|
+
const trimmed = line.trim();
|
|
310
|
+
if (!trimmed.startsWith("{"))
|
|
311
|
+
continue;
|
|
312
|
+
try {
|
|
313
|
+
const parsed = JSON.parse(trimmed);
|
|
314
|
+
if (isJsonRpcFrame(parsed)) {
|
|
315
|
+
const response = {};
|
|
316
|
+
if (typeof parsed.id === "string" || typeof parsed.id === "number")
|
|
317
|
+
response.id = parsed.id;
|
|
318
|
+
if ("result" in parsed)
|
|
319
|
+
response.result = parsed.result;
|
|
320
|
+
if (isRecord(parsed.error)) {
|
|
321
|
+
response.error = {};
|
|
322
|
+
if (typeof parsed.error.code === "number")
|
|
323
|
+
response.error.code = parsed.error.code;
|
|
324
|
+
if (typeof parsed.error.message === "string")
|
|
325
|
+
response.error.message = parsed.error.message;
|
|
326
|
+
}
|
|
327
|
+
responses.push(response);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
// Ignore non-JSON logs from MCP child processes.
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return responses;
|
|
335
|
+
}
|
|
336
|
+
function findInternalJsonRpcError(responses) {
|
|
337
|
+
return responses.find((response) => response.error?.code === JSON_RPC_INTERNAL_ERROR_CODE ||
|
|
338
|
+
/internal error/i.test(response.error?.message ?? ""));
|
|
339
|
+
}
|
|
340
|
+
function findJsonRpcResponseById(responses, id) {
|
|
341
|
+
return responses.find((response) => response.id === id);
|
|
342
|
+
}
|
|
343
|
+
function extractInitializeServerInfo(response) {
|
|
344
|
+
if (!response || !isRecord(response.result))
|
|
345
|
+
return undefined;
|
|
346
|
+
const serverInfo = response.result.serverInfo;
|
|
347
|
+
if (!isRecord(serverInfo))
|
|
348
|
+
return undefined;
|
|
349
|
+
return typeof serverInfo.name === "string" && serverInfo.name.trim().length > 0
|
|
350
|
+
&& typeof serverInfo.version === "string" && serverInfo.version.trim().length > 0
|
|
351
|
+
? { name: serverInfo.name, version: serverInfo.version }
|
|
352
|
+
: undefined;
|
|
353
|
+
}
|
|
354
|
+
function stringifyToolResultContent(result) {
|
|
355
|
+
if (!isRecord(result) || !Array.isArray(result.content))
|
|
356
|
+
return "";
|
|
357
|
+
return result.content
|
|
358
|
+
.map((item) => {
|
|
359
|
+
if (isRecord(item) && typeof item.text === "string")
|
|
360
|
+
return item.text;
|
|
361
|
+
return "";
|
|
362
|
+
})
|
|
363
|
+
.join("\n");
|
|
364
|
+
}
|
|
365
|
+
function isRecord(value) {
|
|
366
|
+
return typeof value === "object" && value !== null;
|
|
367
|
+
}
|
|
368
|
+
async function testRemoteMcpServer(serverName, server) {
|
|
369
|
+
console.log("");
|
|
370
|
+
const url = server.url;
|
|
371
|
+
if (!url) {
|
|
372
|
+
console.error(status.error(`Remote MCP server ${serverName} has no URL`));
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
const urlCheck = validateRemoteMcpUrl(url);
|
|
376
|
+
if (!urlCheck.ok) {
|
|
377
|
+
console.error(status.error(`Invalid remote MCP URL for ${serverName}: ${urlCheck.message}`));
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
const timeoutMs = Math.max(1000, Math.min((server.startup_timeout_sec ?? 20) * 1000, 20_000));
|
|
381
|
+
console.log(style.gray(`Remote MCP JSON-RPC initialize test (${timeoutMs}ms timeout)...`));
|
|
382
|
+
const session = new McpClientSession({
|
|
383
|
+
name: serverName,
|
|
384
|
+
transport: "streamable-http",
|
|
385
|
+
url,
|
|
386
|
+
headers: server.headers,
|
|
387
|
+
http_headers: server.http_headers,
|
|
388
|
+
startupTimeoutMs: timeoutMs,
|
|
389
|
+
requestTimeoutMs: timeoutMs,
|
|
390
|
+
});
|
|
391
|
+
try {
|
|
392
|
+
await session.connect();
|
|
393
|
+
const result = await session.initialize({
|
|
394
|
+
protocolVersion: "2024-11-05",
|
|
395
|
+
capabilities: {},
|
|
396
|
+
clientInfo: {
|
|
397
|
+
name: "omk-mcp-test",
|
|
398
|
+
version: "0.0.0",
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
console.log(status.ok(`Remote MCP initialize succeeded: ${maskSensitiveText(result.serverInfo?.name ?? "unknown")} ${maskSensitiveText(result.serverInfo?.version ?? "")}`.trim()));
|
|
402
|
+
if (serverName === "railway") {
|
|
403
|
+
console.log(style.gray("Railway may prompt OAuth on first tool call; no API token is required in MCP config."));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
catch (err) {
|
|
407
|
+
const message = maskSensitiveText(err instanceof Error ? err.message : String(err));
|
|
408
|
+
if (/HTTP\s*401|unauthorized/i.test(message)) {
|
|
409
|
+
console.error(status.error("Remote MCP initialize failed: authentication required (HTTP 401)."));
|
|
410
|
+
if (serverName === "railway") {
|
|
411
|
+
console.error(style.gray("Railway OAuth may need re-authentication. Run the server locally or re-authorize via browser."));
|
|
412
|
+
}
|
|
413
|
+
else if (serverName === "supabase") {
|
|
414
|
+
console.error(style.gray("Supabase token may be expired. Generate a new access token in the Supabase account dashboard and update your global MCP env."));
|
|
415
|
+
}
|
|
416
|
+
else if (serverName === "github") {
|
|
417
|
+
console.error(style.gray("GitHub token may be expired or lack required scopes. Check your token at https://github.com/settings/tokens."));
|
|
418
|
+
}
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
if (/HTTP\s*403|forbidden/i.test(message)) {
|
|
422
|
+
console.error(status.error("Remote MCP initialize failed: forbidden (HTTP 403). Check token permissions."));
|
|
423
|
+
if (serverName === "railway") {
|
|
424
|
+
console.error(style.gray("Railway OAuth may need re-authentication. Run the server locally or re-authorize via browser."));
|
|
425
|
+
}
|
|
426
|
+
else if (serverName === "supabase") {
|
|
427
|
+
console.error(style.gray("Supabase token may be expired. Generate a new access token in the Supabase account dashboard and update your global MCP env."));
|
|
428
|
+
}
|
|
429
|
+
else if (serverName === "github") {
|
|
430
|
+
console.error(style.gray("GitHub token may be expired or lack required scopes. Check your token at https://github.com/settings/tokens."));
|
|
431
|
+
}
|
|
432
|
+
process.exit(1);
|
|
433
|
+
}
|
|
434
|
+
if (/HTTP\s*404|not found/i.test(message)) {
|
|
435
|
+
console.error(status.error("Remote MCP initialize failed: endpoint not found (HTTP 404)."));
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
if (/HTTP\s*405|method not allowed/i.test(message)) {
|
|
439
|
+
console.error(status.error("Remote MCP initialize failed: method not allowed (HTTP 405). The endpoint may not support MCP over HTTP."));
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
if (/HTTP\s*400|bad request/i.test(message)) {
|
|
443
|
+
console.error(status.error("Remote MCP initialize failed: bad request (HTTP 400). Check URL and headers."));
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
if (/timed?\s*out|timeout|ETIMEDOUT|MCP request timed out|MCP transport send timed out|MCP initialize response missing serverInfo/i.test(message)) {
|
|
447
|
+
console.error(status.error(`Remote MCP initialize failed: ${message}`));
|
|
448
|
+
console.error(style.gray("The endpoint may not be an MCP server, or it may require different headers or a different URL path."));
|
|
449
|
+
process.exit(1);
|
|
450
|
+
}
|
|
451
|
+
console.error(status.error(`Remote MCP initialize failed: ${message}`));
|
|
452
|
+
process.exit(1);
|
|
453
|
+
}
|
|
454
|
+
finally {
|
|
455
|
+
await session.close().catch(() => { });
|
|
456
|
+
}
|
|
457
|
+
}
|