supipowers 2.0.1 → 2.1.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.
- package/README.md +10 -6
- package/package.json +4 -2
- package/skills/harness/SKILL.md +1 -0
- package/src/bootstrap.ts +5 -133
- package/src/commands/clear.ts +6 -6
- package/src/commands/release.ts +3 -1
- package/src/commands/update.ts +1 -1
- package/src/config/defaults.ts +5 -5
- package/src/config/loader.ts +1 -0
- package/src/config/schema.ts +2 -6
- package/src/context/analyzer.ts +104 -35
- package/src/context-mode/knowledge/store.ts +381 -43
- package/src/context-mode/tools.ts +41 -3
- package/src/deps/registry.ts +1 -12
- package/src/fix-pr/assessment.ts +1 -0
- package/src/fix-pr/prompt-builder.ts +1 -0
- package/src/git/commit.ts +76 -18
- package/src/harness/command.ts +103 -6
- package/src/harness/default-agents/docs.md +39 -0
- package/src/harness/docs/config.ts +29 -0
- package/src/harness/docs/glob-match.ts +27 -0
- package/src/harness/docs/index-renderer.ts +82 -0
- package/src/harness/docs/provenance.ts +125 -0
- package/src/harness/docs/regen-decision.ts +167 -0
- package/src/harness/docs/representative-files.ts +175 -0
- package/src/harness/docs/source-hash.ts +106 -0
- package/src/harness/docs/validator.ts +233 -0
- package/src/harness/hooks/layer-context-inject.ts +35 -1
- package/src/harness/hooks/register.ts +24 -3
- package/src/harness/pipeline.ts +20 -5
- package/src/harness/pr-comment/baseline.ts +105 -0
- package/src/harness/pr-comment/ci-env.ts +120 -0
- package/src/harness/pr-comment/gh-poster.ts +227 -0
- package/src/harness/pr-comment/handler.ts +198 -0
- package/src/harness/pr-comment/render.ts +297 -0
- package/src/harness/pr-comment/status.ts +95 -0
- package/src/harness/pr-comment/types.ts +73 -0
- package/src/harness/pr-comment/workflow-summary.ts +47 -0
- package/src/harness/project-paths.ts +95 -0
- package/src/harness/stages/design.ts +1 -0
- package/src/harness/stages/discover.ts +1 -13
- package/src/harness/stages/docs.ts +708 -0
- package/src/harness/stages/implement-apply.ts +877 -0
- package/src/harness/stages/implement.ts +64 -51
- package/src/harness/stages/plan.ts +25 -16
- package/src/harness/stages/validate.ts +370 -0
- package/src/harness/storage.ts +142 -0
- package/src/harness/tools.ts +130 -0
- package/src/mempalace/bridge.ts +207 -41
- package/src/mempalace/config.ts +10 -4
- package/src/mempalace/format.ts +122 -6
- package/src/mempalace/hooks.ts +204 -56
- package/src/mempalace/installer-helper.ts +18 -4
- package/src/mempalace/python/mempalace_bridge.py +128 -3
- package/src/mempalace/runtime.ts +55 -18
- package/src/mempalace/schema.ts +151 -30
- package/src/mempalace/session-summary.ts +5 -0
- package/src/mempalace/tool.ts +17 -4
- package/src/mempalace/upstream-limits.ts +69 -0
- package/src/planning/approval-flow.ts +25 -2
- package/src/planning/planning-ask-tool.ts +34 -4
- package/src/planning/system-prompt.ts +1 -1
- package/src/tool-catalog/active-tool-controller.ts +0 -22
- package/src/tool-catalog/active-tool-planner.ts +0 -26
- package/src/tool-catalog/tool-groups.ts +1 -9
- package/src/types.ts +87 -8
- package/src/ui-design/session.ts +114 -10
- package/src/utils/executable.ts +10 -1
- package/src/workspace/state-paths.ts +1 -1
- package/src/commands/mcp.ts +0 -814
- package/src/mcp/activation.ts +0 -77
- package/src/mcp/config.ts +0 -223
- package/src/mcp/docs.ts +0 -154
- package/src/mcp/gateway.ts +0 -103
- package/src/mcp/lifecycle.ts +0 -79
- package/src/mcp/manager-tool.ts +0 -104
- package/src/mcp/mcpc.ts +0 -113
- package/src/mcp/registry.ts +0 -98
- package/src/mcp/triggers.ts +0 -62
- package/src/mcp/types.ts +0 -95
package/src/commands/mcp.ts
DELETED
|
@@ -1,814 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import type { Platform, PlatformContext } from "../platform/types.js";
|
|
4
|
-
import { loadMcpRegistry, addServer, removeServer, updateServer, getServerConfig, acquireLock, discoverHostMcpServers } from "../mcp/config.js";
|
|
5
|
-
import { McpcClient } from "../mcp/mcpc.js";
|
|
6
|
-
import { generateTriggers } from "../mcp/triggers.js";
|
|
7
|
-
import { generateReadme, writeReadme, writeToolsCache, generateSkill, writeSkill, updateAgentsMd } from "../mcp/docs.js";
|
|
8
|
-
import { MCPC_EXIT } from "../mcp/types.js";
|
|
9
|
-
import type { McpTool, ServerConfig, HostMcpServer } from "../mcp/types.js";
|
|
10
|
-
import { lookupMcpServer, pickBestMatch } from "../mcp/registry.js";
|
|
11
|
-
import { modelRegistry } from "../config/model-registry-instance.js";
|
|
12
|
-
import { resolveModelForAction, createModelBridge, applyModelOverride } from "../config/model-resolver.js";
|
|
13
|
-
import { loadModelConfig } from "../config/model-config.js";
|
|
14
|
-
|
|
15
|
-
modelRegistry.register({
|
|
16
|
-
id: "mcp",
|
|
17
|
-
category: "command",
|
|
18
|
-
label: "MCP",
|
|
19
|
-
harnessRoleHint: "slow",
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
export interface ParsedMcpArgs {
|
|
23
|
-
subcommand?: string;
|
|
24
|
-
name?: string;
|
|
25
|
-
url?: string;
|
|
26
|
-
transport?: string;
|
|
27
|
-
command?: string;
|
|
28
|
-
commandArgs?: string[];
|
|
29
|
-
activation?: string;
|
|
30
|
-
taggable?: boolean;
|
|
31
|
-
json?: boolean;
|
|
32
|
-
docsUrl?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function parseCliArgs(input: string): ParsedMcpArgs {
|
|
36
|
-
const tokens = input.trim().split(/\s+/).filter(Boolean);
|
|
37
|
-
if (tokens.length === 0) return {};
|
|
38
|
-
|
|
39
|
-
const result: ParsedMcpArgs = { subcommand: tokens[0] };
|
|
40
|
-
let i = 1;
|
|
41
|
-
|
|
42
|
-
// Parse flags
|
|
43
|
-
while (i < tokens.length && tokens[i].startsWith("--")) {
|
|
44
|
-
const flag = tokens[i].slice(2);
|
|
45
|
-
if (flag === "json") { result.json = true; i++; continue; }
|
|
46
|
-
if (flag === "transport" && i + 1 < tokens.length) { result.transport = tokens[++i]; i++; continue; }
|
|
47
|
-
if (flag === "docs" && i + 1 < tokens.length) { result.docsUrl = tokens[++i]; i++; continue; }
|
|
48
|
-
i++;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Parse positional args based on subcommand
|
|
52
|
-
switch (result.subcommand) {
|
|
53
|
-
case "add":
|
|
54
|
-
if (i < tokens.length) result.name = tokens[i++];
|
|
55
|
-
if (result.transport === "stdio") {
|
|
56
|
-
if (i < tokens.length) result.command = tokens[i++];
|
|
57
|
-
result.commandArgs = tokens.slice(i);
|
|
58
|
-
} else {
|
|
59
|
-
if (i < tokens.length) result.url = tokens[i++];
|
|
60
|
-
}
|
|
61
|
-
break;
|
|
62
|
-
case "activation":
|
|
63
|
-
if (i < tokens.length) result.name = tokens[i++];
|
|
64
|
-
if (i < tokens.length) result.activation = tokens[i++];
|
|
65
|
-
break;
|
|
66
|
-
case "tag":
|
|
67
|
-
if (i < tokens.length) result.name = tokens[i++];
|
|
68
|
-
if (i < tokens.length) result.taggable = tokens[i] === "on";
|
|
69
|
-
break;
|
|
70
|
-
default:
|
|
71
|
-
if (i < tokens.length) result.name = tokens[i++];
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// ── Helpers ───────────────────────────────────────────────────
|
|
79
|
-
|
|
80
|
-
function createMcpc(platform: Platform): McpcClient {
|
|
81
|
-
return new McpcClient((cmd, args, opts) => platform.exec(cmd, args, opts));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Check mcpc is installed. Returns null with error message if missing. */
|
|
85
|
-
async function ensureMcpc(platform: Platform, ctx: PlatformContext): Promise<McpcClient | null> {
|
|
86
|
-
const mcpc = createMcpc(platform);
|
|
87
|
-
const installed = await mcpc.checkInstalled();
|
|
88
|
-
if (installed.installed) return mcpc;
|
|
89
|
-
|
|
90
|
-
ctx.ui.notify(
|
|
91
|
-
"mcpc is not installed. Run /supi:upgrade to install required tools, or: npm install -g @apify/mcpc",
|
|
92
|
-
"error",
|
|
93
|
-
);
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function buildServerDescription(tools: McpTool[]): string {
|
|
98
|
-
const descs = tools.slice(0, 3).map((t) => t.description).filter(Boolean);
|
|
99
|
-
return descs.length > 0 ? descs.join("; ") : "MCP server";
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function collectServersForSkill(
|
|
103
|
-
platform: Platform,
|
|
104
|
-
cwd: string,
|
|
105
|
-
): Record<string, { tools: McpTool[] }> {
|
|
106
|
-
const registry = loadMcpRegistry(platform.paths, cwd);
|
|
107
|
-
const result: Record<string, { tools: McpTool[] }> = {};
|
|
108
|
-
for (const name of Object.keys(registry.servers)) {
|
|
109
|
-
const toolsPath = path.join(
|
|
110
|
-
platform.paths.project(cwd, "mcpc", name, "tools.json"),
|
|
111
|
-
);
|
|
112
|
-
try {
|
|
113
|
-
const tools = JSON.parse(fs.readFileSync(toolsPath, "utf-8")) as McpTool[];
|
|
114
|
-
result[name] = { tools };
|
|
115
|
-
} catch {
|
|
116
|
-
result[name] = { tools: [] };
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return result;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function collectServersForAgentsMd(
|
|
123
|
-
platform: Platform,
|
|
124
|
-
cwd: string,
|
|
125
|
-
): Record<string, { description: string }> {
|
|
126
|
-
const registry = loadMcpRegistry(platform.paths, cwd);
|
|
127
|
-
const result: Record<string, { description: string }> = {};
|
|
128
|
-
for (const [name, config] of Object.entries(registry.servers)) {
|
|
129
|
-
if (!config.enabled) continue;
|
|
130
|
-
const toolsPath = platform.paths.project(cwd, "mcpc", name, "tools.json");
|
|
131
|
-
let tools: McpTool[] = [];
|
|
132
|
-
try {
|
|
133
|
-
tools = JSON.parse(fs.readFileSync(toolsPath, "utf-8")) as McpTool[];
|
|
134
|
-
} catch { /* no cache yet */ }
|
|
135
|
-
result[name] = { description: buildServerDescription(tools) };
|
|
136
|
-
}
|
|
137
|
-
return result;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function regenerateArtifacts(platform: Platform, cwd: string): void {
|
|
141
|
-
const basePath = platform.paths.project(cwd, "");
|
|
142
|
-
const skillServers = collectServersForSkill(platform, cwd);
|
|
143
|
-
writeSkill(basePath, generateSkill(skillServers));
|
|
144
|
-
updateAgentsMd(cwd, collectServersForAgentsMd(platform, cwd));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ── CLI Dispatch ──────────────────────────────────────────────
|
|
148
|
-
|
|
149
|
-
export async function handleMcpCli(
|
|
150
|
-
platform: Platform,
|
|
151
|
-
ctx: PlatformContext,
|
|
152
|
-
parsed: ParsedMcpArgs,
|
|
153
|
-
): Promise<void> {
|
|
154
|
-
const { paths } = platform;
|
|
155
|
-
const { cwd } = ctx;
|
|
156
|
-
|
|
157
|
-
switch (parsed.subcommand) {
|
|
158
|
-
// ── ADD ────────────────────────────────────────────────
|
|
159
|
-
case "add": {
|
|
160
|
-
if (!parsed.name) {
|
|
161
|
-
ctx.ui.notify("Usage: /supi:mcp add <name> <url>", "warning");
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// No URL/command — look up in registry, then prompt, then agentic fallback
|
|
166
|
-
if (!parsed.url && !parsed.command) {
|
|
167
|
-
ctx.ui.notify(`Looking up "${parsed.name}" in MCP registry...`, "info");
|
|
168
|
-
|
|
169
|
-
// Tier 1: Official MCP Registry
|
|
170
|
-
const results = await lookupMcpServer(
|
|
171
|
-
(cmd, args) => platform.exec(cmd, args),
|
|
172
|
-
parsed.name,
|
|
173
|
-
);
|
|
174
|
-
const match = pickBestMatch(results, parsed.name);
|
|
175
|
-
|
|
176
|
-
if (match) {
|
|
177
|
-
// Found in registry — confirm with user
|
|
178
|
-
const summary = `${match.title} (${match.url})${match.authRequired ? " [auth required]" : ""}`;
|
|
179
|
-
ctx.ui.notify(`Found: ${summary}`, "info");
|
|
180
|
-
|
|
181
|
-
if (ctx.ui.confirm) {
|
|
182
|
-
const confirmed = await ctx.ui.confirm("Add MCP Server", `Add ${match.title}?\n${match.url}`);
|
|
183
|
-
if (!confirmed) {
|
|
184
|
-
ctx.ui.notify("Cancelled", "info");
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Re-enter add flow with the resolved URL
|
|
190
|
-
parsed.url = match.url;
|
|
191
|
-
parsed.transport = match.transport;
|
|
192
|
-
parsed.docsUrl = match.docsUrl;
|
|
193
|
-
// Fall through to the normal add logic below
|
|
194
|
-
} else if (ctx.hasUI) {
|
|
195
|
-
// Tier 2: Not in registry — ask user for URL
|
|
196
|
-
ctx.ui.notify(`"${parsed.name}" not found in registry`, "warning");
|
|
197
|
-
const manualUrl = await ctx.ui.input("Server URL (or leave empty to search with agent):", {});
|
|
198
|
-
|
|
199
|
-
if (manualUrl && manualUrl.trim()) {
|
|
200
|
-
parsed.url = manualUrl.trim();
|
|
201
|
-
// Fall through to normal add logic
|
|
202
|
-
} else {
|
|
203
|
-
// Tier 3: Agentic search — last resort
|
|
204
|
-
platform.sendMessage({
|
|
205
|
-
customType: "supi-mcp-search",
|
|
206
|
-
content: `The user wants to add an MCP server called "${parsed.name}" but it wasn't found in the official MCP registry. Search for the official endpoint — check GitHub, project docs, or other sources. Once found, use the mcpc_manager tool to add it with action "add". Include a docsUrl if available.`,
|
|
207
|
-
display: true,
|
|
208
|
-
}, { deliverAs: "steer", triggerTurn: true });
|
|
209
|
-
ctx.ui.notify(`Agent searching for "${parsed.name}"...`, "info");
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
} else {
|
|
213
|
-
ctx.ui.notify(`"${parsed.name}" not found. Provide URL: /supi:mcp add ${parsed.name} <url>`, "warning");
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const lock = acquireLock(paths, cwd);
|
|
219
|
-
if (!lock.acquired) {
|
|
220
|
-
ctx.ui.notify("Another MCP operation is in progress", "warning");
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
try {
|
|
225
|
-
const serverPartial: Partial<ServerConfig> = {
|
|
226
|
-
transport: (parsed.transport ?? "http") as "http" | "stdio",
|
|
227
|
-
};
|
|
228
|
-
if (parsed.url) serverPartial.url = parsed.url;
|
|
229
|
-
if (parsed.command) {
|
|
230
|
-
serverPartial.command = parsed.command;
|
|
231
|
-
serverPartial.args = parsed.commandArgs ?? [];
|
|
232
|
-
}
|
|
233
|
-
if (parsed.docsUrl) serverPartial.docsUrl = parsed.docsUrl;
|
|
234
|
-
|
|
235
|
-
const addResult = addServer(paths, cwd, parsed.name, serverPartial);
|
|
236
|
-
if (!addResult.ok) {
|
|
237
|
-
ctx.ui.notify(`Failed to add server: ${addResult.reason}`, "error");
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Ensure mcpc is installed
|
|
242
|
-
const mcpc = await ensureMcpc(platform, ctx);
|
|
243
|
-
if (!mcpc) return;
|
|
244
|
-
|
|
245
|
-
// Connect and fetch tools
|
|
246
|
-
const target = parsed.url ?? parsed.command ?? parsed.name;
|
|
247
|
-
const connectResult = await mcpc.connect(target, parsed.name);
|
|
248
|
-
if (connectResult.code !== MCPC_EXIT.SUCCESS) {
|
|
249
|
-
const detail = connectResult.output.trim() || `exit code ${connectResult.code}`;
|
|
250
|
-
ctx.ui.notify(`Server added but connection failed: ${detail}`, "warning");
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const toolsResult = await mcpc.toolsList(parsed.name);
|
|
255
|
-
const tools = toolsResult.tools;
|
|
256
|
-
|
|
257
|
-
// Generate triggers and update config
|
|
258
|
-
const triggers = generateTriggers(parsed.name, tools);
|
|
259
|
-
updateServer(paths, cwd, parsed.name, { triggers });
|
|
260
|
-
|
|
261
|
-
// Write artifacts
|
|
262
|
-
const basePath = platform.paths.project(cwd, "");
|
|
263
|
-
writeToolsCache(basePath, parsed.name, tools);
|
|
264
|
-
|
|
265
|
-
const config = getServerConfig(paths, cwd, parsed.name)!;
|
|
266
|
-
const readme = generateReadme(parsed.name, config, tools);
|
|
267
|
-
writeReadme(basePath, parsed.name, readme);
|
|
268
|
-
|
|
269
|
-
// Regenerate skill and AGENTS.md
|
|
270
|
-
regenerateArtifacts(platform, cwd);
|
|
271
|
-
|
|
272
|
-
ctx.ui.notify(`Added server "${parsed.name}" with ${tools.length} tools`, "info");
|
|
273
|
-
} finally {
|
|
274
|
-
lock.release();
|
|
275
|
-
}
|
|
276
|
-
break;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// ── REMOVE ─────────────────────────────────────────────
|
|
280
|
-
case "remove": {
|
|
281
|
-
if (!parsed.name) {
|
|
282
|
-
ctx.ui.notify("Usage: /supi:mcp remove <name>", "warning");
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
removeServer(paths, cwd, parsed.name);
|
|
287
|
-
|
|
288
|
-
// Clean up mcpc/<name>/ directory
|
|
289
|
-
const serverDir = platform.paths.project(cwd, "mcpc", parsed.name);
|
|
290
|
-
if (fs.existsSync(serverDir)) {
|
|
291
|
-
fs.rmSync(serverDir, { recursive: true });
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
regenerateArtifacts(platform, cwd);
|
|
295
|
-
ctx.ui.notify(`Removed server "${parsed.name}"`, "info");
|
|
296
|
-
break;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// ── ENABLE ─────────────────────────────────────────────
|
|
300
|
-
case "enable": {
|
|
301
|
-
if (!parsed.name) {
|
|
302
|
-
ctx.ui.notify("Usage: /supi:mcp enable <name>", "warning");
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
const result = updateServer(paths, cwd, parsed.name, { enabled: true });
|
|
306
|
-
if (!result.ok) {
|
|
307
|
-
ctx.ui.notify(result.reason ?? "Failed to enable server", "error");
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
regenerateArtifacts(platform, cwd);
|
|
311
|
-
ctx.ui.notify(`Enabled server "${parsed.name}"`, "info");
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// ── DISABLE ────────────────────────────────────────────
|
|
316
|
-
case "disable": {
|
|
317
|
-
if (!parsed.name) {
|
|
318
|
-
ctx.ui.notify("Usage: /supi:mcp disable <name>", "warning");
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
const result = updateServer(paths, cwd, parsed.name, { enabled: false });
|
|
322
|
-
if (!result.ok) {
|
|
323
|
-
ctx.ui.notify(result.reason ?? "Failed to disable server", "error");
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
regenerateArtifacts(platform, cwd);
|
|
327
|
-
ctx.ui.notify(`Disabled server "${parsed.name}"`, "info");
|
|
328
|
-
break;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// ── REFRESH ────────────────────────────────────────────
|
|
332
|
-
case "refresh": {
|
|
333
|
-
const mcpc = await ensureMcpc(platform, ctx);
|
|
334
|
-
if (!mcpc) return;
|
|
335
|
-
const registry = loadMcpRegistry(paths, cwd);
|
|
336
|
-
|
|
337
|
-
const names = parsed.name
|
|
338
|
-
? [parsed.name]
|
|
339
|
-
: Object.keys(registry.servers);
|
|
340
|
-
|
|
341
|
-
for (const name of names) {
|
|
342
|
-
const config = registry.servers[name];
|
|
343
|
-
if (!config) {
|
|
344
|
-
ctx.ui.notify(`Server "${name}" not found`, "warning");
|
|
345
|
-
continue;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const target = config.url ?? config.command ?? name;
|
|
349
|
-
const connectResult = await mcpc.connect(target, name);
|
|
350
|
-
if (connectResult.code !== MCPC_EXIT.SUCCESS) {
|
|
351
|
-
ctx.ui.notify(`Refresh failed for "${name}": ${connectResult.output}`, "warning");
|
|
352
|
-
continue;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const toolsResult = await mcpc.toolsList(name);
|
|
356
|
-
const tools = toolsResult.tools;
|
|
357
|
-
|
|
358
|
-
const triggers = generateTriggers(name, tools);
|
|
359
|
-
updateServer(paths, cwd, name, { triggers });
|
|
360
|
-
|
|
361
|
-
const basePath = platform.paths.project(cwd, "");
|
|
362
|
-
writeToolsCache(basePath, name, tools);
|
|
363
|
-
|
|
364
|
-
const updatedConfig = getServerConfig(paths, cwd, name)!;
|
|
365
|
-
const readme = generateReadme(name, updatedConfig, tools);
|
|
366
|
-
writeReadme(basePath, name, readme);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
regenerateArtifacts(platform, cwd);
|
|
370
|
-
ctx.ui.notify(
|
|
371
|
-
parsed.name
|
|
372
|
-
? `Refreshed server "${parsed.name}"`
|
|
373
|
-
: `Refreshed ${names.length} server(s)`,
|
|
374
|
-
"info",
|
|
375
|
-
);
|
|
376
|
-
break;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// ── LOGIN ──────────────────────────────────────────────
|
|
380
|
-
case "login": {
|
|
381
|
-
if (!parsed.name) {
|
|
382
|
-
ctx.ui.notify("Usage: /supi:mcp login <name>", "warning");
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
const config = getServerConfig(paths, cwd, parsed.name);
|
|
386
|
-
if (!config) {
|
|
387
|
-
ctx.ui.notify(`Server "${parsed.name}" not found`, "error");
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
const target = config.url ?? config.command ?? parsed.name;
|
|
391
|
-
const mcpc = await ensureMcpc(platform, ctx);
|
|
392
|
-
if (!mcpc) return;
|
|
393
|
-
ctx.ui.notify(`Starting OAuth login for "${parsed.name}"...`, "info");
|
|
394
|
-
const result = await mcpc.login(target);
|
|
395
|
-
if (result.code !== MCPC_EXIT.SUCCESS) {
|
|
396
|
-
const detail = result.output.trim() || `exit code ${result.code}`;
|
|
397
|
-
ctx.ui.notify(`Login failed: ${detail}`, "error");
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
ctx.ui.notify(`Logged in to "${parsed.name}"`, "info");
|
|
401
|
-
break;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// ── LOGOUT ─────────────────────────────────────────────
|
|
405
|
-
case "logout": {
|
|
406
|
-
if (!parsed.name) {
|
|
407
|
-
ctx.ui.notify("Usage: /supi:mcp logout <name>", "warning");
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
const config = getServerConfig(paths, cwd, parsed.name);
|
|
411
|
-
if (!config) {
|
|
412
|
-
ctx.ui.notify(`Server "${parsed.name}" not found`, "error");
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
const target = config.url ?? config.command ?? parsed.name;
|
|
416
|
-
const mcpc = await ensureMcpc(platform, ctx);
|
|
417
|
-
if (!mcpc) return;
|
|
418
|
-
const result = await mcpc.logout(target);
|
|
419
|
-
if (result.code !== MCPC_EXIT.SUCCESS) {
|
|
420
|
-
const detail = result.output.trim() || `exit code ${result.code}`;
|
|
421
|
-
ctx.ui.notify(`Logout failed: ${detail}`, "error");
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
ctx.ui.notify(`Logged out of "${parsed.name}"`, "info");
|
|
425
|
-
break;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// ── ACTIVATION ─────────────────────────────────────────
|
|
429
|
-
case "activation": {
|
|
430
|
-
if (!parsed.name || !parsed.activation) {
|
|
431
|
-
ctx.ui.notify("Usage: /supi:mcp activation <name> <always|contextual|disabled>", "warning");
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
const result = updateServer(paths, cwd, parsed.name, {
|
|
435
|
-
activation: parsed.activation as ServerConfig["activation"],
|
|
436
|
-
});
|
|
437
|
-
if (!result.ok) {
|
|
438
|
-
ctx.ui.notify(result.reason ?? "Failed to update activation", "error");
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
ctx.ui.notify(`Set activation for "${parsed.name}" to ${parsed.activation}`, "info");
|
|
442
|
-
break;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// ── TAG ────────────────────────────────────────────────
|
|
446
|
-
case "tag": {
|
|
447
|
-
if (!parsed.name || parsed.taggable === undefined) {
|
|
448
|
-
ctx.ui.notify("Usage: /supi:mcp tag <name> <on|off>", "warning");
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
const result = updateServer(paths, cwd, parsed.name, {
|
|
452
|
-
taggable: parsed.taggable,
|
|
453
|
-
});
|
|
454
|
-
if (!result.ok) {
|
|
455
|
-
ctx.ui.notify(result.reason ?? "Failed to update taggable", "error");
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
ctx.ui.notify(`Set taggable for "${parsed.name}" to ${parsed.taggable ? "on" : "off"}`, "info");
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// ── LIST ───────────────────────────────────────────────
|
|
463
|
-
case "list": {
|
|
464
|
-
const registry = loadMcpRegistry(paths, cwd);
|
|
465
|
-
const entries = Object.entries(registry.servers);
|
|
466
|
-
|
|
467
|
-
if (entries.length === 0) {
|
|
468
|
-
ctx.ui.notify("No MCP servers configured", "info");
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
if (parsed.json) {
|
|
473
|
-
ctx.ui.notify(JSON.stringify(registry.servers, null, 2), "info");
|
|
474
|
-
} else {
|
|
475
|
-
const lines = entries.map(([name, config]) => {
|
|
476
|
-
const status = config.enabled ? "enabled" : "disabled";
|
|
477
|
-
const transport = config.transport.toUpperCase();
|
|
478
|
-
const activation = config.activation;
|
|
479
|
-
return ` ${name} [${transport}] ${status} (${activation})`;
|
|
480
|
-
});
|
|
481
|
-
ctx.ui.notify("MCP Servers:\n" + lines.join("\n"), "info");
|
|
482
|
-
}
|
|
483
|
-
break;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// ── INFO ───────────────────────────────────────────────
|
|
487
|
-
case "info": {
|
|
488
|
-
if (!parsed.name) {
|
|
489
|
-
ctx.ui.notify("Usage: /supi:mcp info <name>", "warning");
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
const config = getServerConfig(paths, cwd, parsed.name);
|
|
493
|
-
if (!config) {
|
|
494
|
-
ctx.ui.notify(`Server "${parsed.name}" not found`, "error");
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const toolsPath = platform.paths.project(cwd, "mcpc", parsed.name, "tools.json");
|
|
499
|
-
let tools: McpTool[] = [];
|
|
500
|
-
try {
|
|
501
|
-
tools = JSON.parse(fs.readFileSync(toolsPath, "utf-8")) as McpTool[];
|
|
502
|
-
} catch { /* no cache */ }
|
|
503
|
-
|
|
504
|
-
const lines: string[] = [];
|
|
505
|
-
lines.push(`Server: ${parsed.name}`);
|
|
506
|
-
if (config.url) lines.push(`URL: ${config.url}`);
|
|
507
|
-
if (config.command) lines.push(`Command: ${config.command} ${(config.args ?? []).join(" ")}`);
|
|
508
|
-
lines.push(`Transport: ${config.transport.toUpperCase()}`);
|
|
509
|
-
lines.push(`Activation: ${config.activation}`);
|
|
510
|
-
lines.push(`Enabled: ${config.enabled}`);
|
|
511
|
-
lines.push(`Taggable: ${config.taggable}`);
|
|
512
|
-
lines.push(`Added: ${config.addedAt}`);
|
|
513
|
-
if (config.triggers?.length > 0) lines.push(`Triggers: ${config.triggers.join(", ")}`);
|
|
514
|
-
lines.push(`Tools (${tools.length}): ${tools.map((t) => t.name).join(", ") || "none cached"}`);
|
|
515
|
-
|
|
516
|
-
ctx.ui.notify(lines.join("\n"), "info");
|
|
517
|
-
break;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// ── MIGRATE ──────────────────────────────────────────
|
|
521
|
-
case "migrate": {
|
|
522
|
-
await handleMcpMigrate(platform, ctx);
|
|
523
|
-
break;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
default:
|
|
527
|
-
ctx.ui.notify(
|
|
528
|
-
`Unknown subcommand "${parsed.subcommand}". Available: add, remove, enable, disable, refresh, login, logout, activation, tag, list, info, migrate`,
|
|
529
|
-
"warning",
|
|
530
|
-
);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// ── Migrate Handler ───────────────────────────────────────────
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* Discover MCP servers from the host config that are not yet in supi's
|
|
538
|
-
* managed registry, present a checkbox multi-select UI, and migrate selected
|
|
539
|
-
* servers via the existing "add" flow.
|
|
540
|
-
*/
|
|
541
|
-
export async function handleMcpMigrate(
|
|
542
|
-
platform: Platform,
|
|
543
|
-
ctx: PlatformContext,
|
|
544
|
-
): Promise<void> {
|
|
545
|
-
// 1. Discover host servers
|
|
546
|
-
const hostServers = discoverHostMcpServers(platform.paths, ctx.cwd);
|
|
547
|
-
|
|
548
|
-
// 2. Load supi registry to find already-managed names
|
|
549
|
-
const registry = loadMcpRegistry(platform.paths, ctx.cwd);
|
|
550
|
-
const managedNames = new Set(Object.keys(registry.servers));
|
|
551
|
-
|
|
552
|
-
// 3. Filter to servers not yet managed
|
|
553
|
-
const candidates: HostMcpServer[] = hostServers.filter((s) => !managedNames.has(s.name));
|
|
554
|
-
|
|
555
|
-
if (candidates.length === 0) {
|
|
556
|
-
ctx.ui.notify("All host MCP servers are already in the supipowers registry.", "info");
|
|
557
|
-
return;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// 4. Checkbox-toggle multi-select UI
|
|
561
|
-
const selected = new Set<number>();
|
|
562
|
-
|
|
563
|
-
while (true) {
|
|
564
|
-
const checkboxOptions = candidates.map((s, i) => {
|
|
565
|
-
const tick = selected.has(i) ? "✓" : "✗";
|
|
566
|
-
// Treat sse as http for display
|
|
567
|
-
const t = s.transport === "sse" ? "http" : s.transport;
|
|
568
|
-
const auth = s.hasAuth ? " 🔒" : "";
|
|
569
|
-
return `${tick} ${s.name} — ${t} (${s.scope})${auth}`;
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
const selectedCount = selected.size;
|
|
573
|
-
const doneLabel = `[Done — migrate ${selectedCount} selected]`;
|
|
574
|
-
|
|
575
|
-
checkboxOptions.push("[Select All]");
|
|
576
|
-
checkboxOptions.push(doneLabel);
|
|
577
|
-
checkboxOptions.push("[Cancel]");
|
|
578
|
-
|
|
579
|
-
const pick = await ctx.ui.select(
|
|
580
|
-
"Migrate MCP servers from host config",
|
|
581
|
-
checkboxOptions,
|
|
582
|
-
{ helpText: "Select servers to migrate · Esc to cancel" },
|
|
583
|
-
);
|
|
584
|
-
|
|
585
|
-
if (pick === undefined || pick === null || pick === "[Cancel]") return;
|
|
586
|
-
|
|
587
|
-
if (pick === "[Select All]") {
|
|
588
|
-
if (selected.size === candidates.length) {
|
|
589
|
-
// All selected — deselect all
|
|
590
|
-
selected.clear();
|
|
591
|
-
} else {
|
|
592
|
-
candidates.forEach((_, i) => selected.add(i));
|
|
593
|
-
}
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
if (pick === doneLabel) break;
|
|
598
|
-
|
|
599
|
-
// Toggle the clicked candidate
|
|
600
|
-
const idx = checkboxOptions.indexOf(pick);
|
|
601
|
-
if (idx >= 0 && idx < candidates.length) {
|
|
602
|
-
if (selected.has(idx)) {
|
|
603
|
-
selected.delete(idx);
|
|
604
|
-
} else {
|
|
605
|
-
selected.add(idx);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
if (selected.size === 0) {
|
|
611
|
-
ctx.ui.notify("No servers selected. Migration cancelled.", "info");
|
|
612
|
-
return;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
// 5. Migrate selected servers via the existing add flow
|
|
616
|
-
const toMigrate = [...selected].map((i) => candidates[i]);
|
|
617
|
-
let migratedCount = 0;
|
|
618
|
-
|
|
619
|
-
for (const server of toMigrate) {
|
|
620
|
-
// Map host transport to supi's transport (sse → http, treat as HTTP URL-based)
|
|
621
|
-
const transport: "http" | "stdio" = server.transport === "stdio" ? "stdio" : "http";
|
|
622
|
-
|
|
623
|
-
await handleMcpCli(platform, ctx, {
|
|
624
|
-
subcommand: "add",
|
|
625
|
-
name: server.name,
|
|
626
|
-
url: transport === "http" ? server.url : undefined,
|
|
627
|
-
command: transport === "stdio" ? server.command : undefined,
|
|
628
|
-
commandArgs: server.args,
|
|
629
|
-
transport,
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
migratedCount++;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
ctx.ui.notify(`Migration complete. Processed ${migratedCount} server(s).`, "info");
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
export function handleMcp(platform: Platform, ctx: PlatformContext): void {
|
|
640
|
-
if (!ctx.hasUI) {
|
|
641
|
-
ctx.ui.notify("MCP UI requires interactive mode", "warning");
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
void (async () => {
|
|
646
|
-
while (true) {
|
|
647
|
-
const registry = loadMcpRegistry(platform.paths, ctx.cwd);
|
|
648
|
-
const entries = Object.entries(registry.servers);
|
|
649
|
-
|
|
650
|
-
// Build server list options
|
|
651
|
-
const options: string[] = entries.map(([name, config]) => {
|
|
652
|
-
const icon = config.enabled ? "\u25cf" : "\u25cb";
|
|
653
|
-
const status = config.enabled ? "connected" : "disconnected";
|
|
654
|
-
const flags: string[] = [config.activation];
|
|
655
|
-
if (config.taggable) flags.push("$taggable");
|
|
656
|
-
return `${icon} ${name} — ${status} (${flags.join(", ")})`;
|
|
657
|
-
});
|
|
658
|
-
options.push("[Add server]");
|
|
659
|
-
options.push("[Refresh all]");
|
|
660
|
-
options.push("[Migrate from host]");
|
|
661
|
-
options.push("[Done]");
|
|
662
|
-
|
|
663
|
-
const choice = await ctx.ui.select(
|
|
664
|
-
"MCP Servers",
|
|
665
|
-
options,
|
|
666
|
-
{ helpText: "Select a server to manage · Esc to close" },
|
|
667
|
-
);
|
|
668
|
-
|
|
669
|
-
if (choice === undefined || choice === null || choice === "[Done]") break;
|
|
670
|
-
|
|
671
|
-
// ── Add server flow ───────────────────────────────────
|
|
672
|
-
if (choice === "[Add server]") {
|
|
673
|
-
const name = await ctx.ui.input("Server name", { placeholder: "e.g. figma" });
|
|
674
|
-
if (!name) continue;
|
|
675
|
-
|
|
676
|
-
const url = await ctx.ui.input("Server URL", { placeholder: "e.g. https://mcp.figma.com" });
|
|
677
|
-
if (!url) continue;
|
|
678
|
-
|
|
679
|
-
const transport = await ctx.ui.select(
|
|
680
|
-
"Transport",
|
|
681
|
-
["http", "stdio"],
|
|
682
|
-
{ helpText: "How to connect to the server" },
|
|
683
|
-
);
|
|
684
|
-
if (!transport) continue;
|
|
685
|
-
|
|
686
|
-
await handleMcpCli(platform, ctx, {
|
|
687
|
-
subcommand: "add",
|
|
688
|
-
name,
|
|
689
|
-
url: transport === "http" ? url : undefined,
|
|
690
|
-
command: transport === "stdio" ? url : undefined,
|
|
691
|
-
transport,
|
|
692
|
-
});
|
|
693
|
-
continue;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
// ── Refresh all ───────────────────────────────────────
|
|
697
|
-
// ── Migrate from host flow ───────────────────────────
|
|
698
|
-
if (choice === "[Migrate from host]") {
|
|
699
|
-
await handleMcpMigrate(platform, ctx);
|
|
700
|
-
continue;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
if (choice === "[Refresh all]") {
|
|
704
|
-
await handleMcpCli(platform, ctx, { subcommand: "refresh" });
|
|
705
|
-
continue;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
// ── Server action menu ────────────────────────────────
|
|
709
|
-
const serverIndex = options.indexOf(choice);
|
|
710
|
-
if (serverIndex < 0 || serverIndex >= entries.length) continue;
|
|
711
|
-
|
|
712
|
-
const [serverName, serverConfig] = entries[serverIndex];
|
|
713
|
-
|
|
714
|
-
while (true) {
|
|
715
|
-
const serverLabel = serverConfig.url ?? serverConfig.command ?? serverName;
|
|
716
|
-
const toggleLabel = serverConfig.enabled ? "[Disable]" : "[Enable]";
|
|
717
|
-
const actionOptions = [
|
|
718
|
-
toggleLabel,
|
|
719
|
-
"[Refresh tools]",
|
|
720
|
-
"[Login]",
|
|
721
|
-
"[Logout]",
|
|
722
|
-
"[Edit triggers]",
|
|
723
|
-
"[View README]",
|
|
724
|
-
"[Remove]",
|
|
725
|
-
"[Back]",
|
|
726
|
-
];
|
|
727
|
-
|
|
728
|
-
const action = await ctx.ui.select(
|
|
729
|
-
`${serverName} — ${serverLabel}`,
|
|
730
|
-
actionOptions,
|
|
731
|
-
{ helpText: "Select an action" },
|
|
732
|
-
);
|
|
733
|
-
|
|
734
|
-
if (action === undefined || action === null || action === "[Back]") break;
|
|
735
|
-
|
|
736
|
-
switch (action) {
|
|
737
|
-
case "[Enable]":
|
|
738
|
-
await handleMcpCli(platform, ctx, { subcommand: "enable", name: serverName });
|
|
739
|
-
break;
|
|
740
|
-
case "[Disable]":
|
|
741
|
-
await handleMcpCli(platform, ctx, { subcommand: "disable", name: serverName });
|
|
742
|
-
break;
|
|
743
|
-
case "[Refresh tools]":
|
|
744
|
-
await handleMcpCli(platform, ctx, { subcommand: "refresh", name: serverName });
|
|
745
|
-
break;
|
|
746
|
-
case "[Login]":
|
|
747
|
-
await handleMcpCli(platform, ctx, { subcommand: "login", name: serverName });
|
|
748
|
-
break;
|
|
749
|
-
case "[Logout]":
|
|
750
|
-
await handleMcpCli(platform, ctx, { subcommand: "logout", name: serverName });
|
|
751
|
-
break;
|
|
752
|
-
case "[Edit triggers]": {
|
|
753
|
-
const currentTriggers = serverConfig.triggers?.join(", ") ?? "";
|
|
754
|
-
const newTriggers = await ctx.ui.input("Triggers (comma-separated)", {
|
|
755
|
-
placeholder: currentTriggers,
|
|
756
|
-
});
|
|
757
|
-
if (newTriggers !== null && newTriggers !== undefined) {
|
|
758
|
-
const triggerList = newTriggers
|
|
759
|
-
.split(",")
|
|
760
|
-
.map((t) => t.trim())
|
|
761
|
-
.filter(Boolean);
|
|
762
|
-
updateServer(platform.paths, ctx.cwd, serverName, { triggers: triggerList });
|
|
763
|
-
ctx.ui.notify(`Updated triggers for "${serverName}"`, "info");
|
|
764
|
-
}
|
|
765
|
-
break;
|
|
766
|
-
}
|
|
767
|
-
case "[View README]": {
|
|
768
|
-
const readmePath = platform.paths.project(ctx.cwd, "mcpc", serverName, "README.md");
|
|
769
|
-
try {
|
|
770
|
-
const content = fs.readFileSync(readmePath, "utf-8");
|
|
771
|
-
ctx.ui.notify(content, "info");
|
|
772
|
-
} catch {
|
|
773
|
-
ctx.ui.notify(`No README found for "${serverName}"`, "warning");
|
|
774
|
-
}
|
|
775
|
-
break;
|
|
776
|
-
}
|
|
777
|
-
case "[Remove]": {
|
|
778
|
-
const confirmed = ctx.ui.confirm
|
|
779
|
-
? await ctx.ui.confirm("Remove server", `Remove "${serverName}"? This cannot be undone.`)
|
|
780
|
-
: true;
|
|
781
|
-
if (confirmed) {
|
|
782
|
-
await handleMcpCli(platform, ctx, { subcommand: "remove", name: serverName });
|
|
783
|
-
}
|
|
784
|
-
break;
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// Re-read config after action (it may have changed)
|
|
789
|
-
const updatedConfig = getServerConfig(platform.paths, ctx.cwd, serverName);
|
|
790
|
-
if (!updatedConfig) break; // Server was removed
|
|
791
|
-
// Update local reference for toggle label on next iteration
|
|
792
|
-
Object.assign(serverConfig, updatedConfig);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
})();
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
export function registerMcpCommand(platform: Platform): void {
|
|
799
|
-
platform.registerCommand("supi:mcp", {
|
|
800
|
-
description: "Manage MCP servers — add, remove, enable, disable, refresh",
|
|
801
|
-
async handler(args: string | undefined, ctx: any) {
|
|
802
|
-
const modelCfg = loadModelConfig(platform.paths, ctx.cwd);
|
|
803
|
-
const bridge = createModelBridge(platform);
|
|
804
|
-
const resolved = resolveModelForAction("mcp", modelRegistry, modelCfg, bridge);
|
|
805
|
-
await applyModelOverride(platform, ctx, "mcp", resolved);
|
|
806
|
-
if (args) {
|
|
807
|
-
// CLI mode — parse and dispatch
|
|
808
|
-
await handleMcpCli(platform, ctx, parseCliArgs(args));
|
|
809
|
-
} else {
|
|
810
|
-
handleMcp(platform, ctx);
|
|
811
|
-
}
|
|
812
|
-
},
|
|
813
|
-
});
|
|
814
|
-
}
|