@docyrus/docyrus 0.0.33 → 0.0.35
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 +25 -0
- package/agent-loader.js +3 -2
- package/agent-loader.js.map +2 -2
- package/main.js +82252 -46058
- package/main.js.map +4 -4
- package/package.json +12 -3
- package/resources/chrome-tools/browser-content.js +46 -46
- package/resources/chrome-tools/browser-cookies.js +16 -16
- package/resources/chrome-tools/browser-eval.js +27 -27
- package/resources/chrome-tools/browser-hn-scraper.js +1 -1
- package/resources/chrome-tools/browser-nav.js +23 -23
- package/resources/chrome-tools/browser-pick.js +127 -127
- package/resources/chrome-tools/browser-screenshot.js +10 -10
- package/resources/chrome-tools/browser-start.js +38 -38
- package/resources/pi-agent/extensions/answer.ts +392 -384
- package/resources/pi-agent/extensions/context.ts +415 -415
- package/resources/pi-agent/extensions/control.ts +1287 -1287
- package/resources/pi-agent/extensions/diff.ts +171 -171
- package/resources/pi-agent/extensions/files.ts +155 -155
- package/resources/pi-agent/extensions/knowledge.ts +664 -0
- package/resources/pi-agent/extensions/loop.ts +375 -375
- package/resources/pi-agent/extensions/pi-bash-live-view/index.ts +1 -1
- package/resources/pi-agent/extensions/pi-bash-live-view/package.json +22 -22
- package/resources/pi-agent/extensions/pi-bash-live-view/pty-execute.ts +2 -2
- package/resources/pi-agent/extensions/pi-bash-live-view/pty-session.ts +2 -2
- package/resources/pi-agent/extensions/pi-bash-live-view/spawn-helper.ts +2 -2
- package/resources/pi-agent/extensions/pi-bash-live-view/terminal-emulator.ts +18 -18
- package/resources/pi-agent/extensions/pi-bash-live-view/truncate.ts +1 -1
- package/resources/pi-agent/extensions/pi-bash-live-view/widget.ts +4 -4
- package/resources/pi-agent/extensions/pi-custom-compaction/package.json +4 -4
- package/resources/pi-agent/extensions/pi-mcp-adapter/app-bridge.bundle.js +14 -14
- package/resources/pi-agent/extensions/pi-mcp-adapter/commands.ts +6 -6
- package/resources/pi-agent/extensions/pi-mcp-adapter/config.ts +9 -9
- package/resources/pi-agent/extensions/pi-mcp-adapter/consent-manager.ts +4 -4
- package/resources/pi-agent/extensions/pi-mcp-adapter/direct-tools.ts +13 -13
- package/resources/pi-agent/extensions/pi-mcp-adapter/glimpse-ui.ts +5 -5
- package/resources/pi-agent/extensions/pi-mcp-adapter/host-html-template.ts +13 -13
- package/resources/pi-agent/extensions/pi-mcp-adapter/index.ts +14 -14
- package/resources/pi-agent/extensions/pi-mcp-adapter/init.ts +17 -17
- package/resources/pi-agent/extensions/pi-mcp-adapter/lifecycle.ts +2 -2
- package/resources/pi-agent/extensions/pi-mcp-adapter/logger.ts +2 -2
- package/resources/pi-agent/extensions/pi-mcp-adapter/mcp-panel.ts +17 -17
- package/resources/pi-agent/extensions/pi-mcp-adapter/metadata-cache.ts +9 -9
- package/resources/pi-agent/extensions/pi-mcp-adapter/npx-resolver.ts +35 -35
- package/resources/pi-agent/extensions/pi-mcp-adapter/oauth-handler.ts +1 -1
- package/resources/pi-agent/extensions/pi-mcp-adapter/proxy-modes.ts +12 -12
- package/resources/pi-agent/extensions/pi-mcp-adapter/server-manager.ts +6 -6
- package/resources/pi-agent/extensions/pi-mcp-adapter/tool-metadata.ts +4 -4
- package/resources/pi-agent/extensions/pi-mcp-adapter/types.ts +2 -2
- package/resources/pi-agent/extensions/pi-mcp-adapter/ui-resource-handler.ts +6 -6
- package/resources/pi-agent/extensions/pi-mcp-adapter/ui-server.ts +17 -17
- package/resources/pi-agent/extensions/pi-mcp-adapter/ui-session.ts +22 -22
- package/resources/pi-agent/extensions/pi-mcp-adapter/utils.ts +2 -2
- package/resources/pi-agent/extensions/prompt-editor.ts +900 -900
- package/resources/pi-agent/extensions/prompt-url-widget.ts +122 -122
- package/resources/pi-agent/extensions/redraws.ts +14 -14
- package/resources/pi-agent/extensions/review.ts +1533 -1533
- package/resources/pi-agent/extensions/todos.ts +1735 -1735
- package/resources/pi-agent/extensions/tps.ts +40 -40
- package/resources/pi-agent/extensions/whimsical.ts +3 -3
- package/resources/pi-agent/prompts/agent-system.md +2 -0
- package/resources/pi-agent/prompts/coder-system.md +2 -0
- package/resources/pi-agent/skills/officecli/SKILL.md +113 -0
- package/server-loader.js +82 -1
- package/server-loader.js.map +3 -3
- package/tui.mjs +2 -0
- package/tui.mjs.map +1 -1
|
@@ -9,7 +9,7 @@ import { getScopedMcpOAuthTokensPath } from "./paths.js";
|
|
|
9
9
|
import { buildToolMetadata } from "./tool-metadata.js";
|
|
10
10
|
|
|
11
11
|
export async function showStatus(state: McpExtensionState, ctx: ExtensionContext): Promise<void> {
|
|
12
|
-
if (!ctx.hasUI) return;
|
|
12
|
+
if (!ctx.hasUI) {return;}
|
|
13
13
|
|
|
14
14
|
const lines: string[] = ["MCP Server Status:", ""];
|
|
15
15
|
|
|
@@ -45,7 +45,7 @@ export async function showStatus(state: McpExtensionState, ctx: ExtensionContext
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export async function showTools(state: McpExtensionState, ctx: ExtensionContext): Promise<void> {
|
|
48
|
-
if (!ctx.hasUI) return;
|
|
48
|
+
if (!ctx.hasUI) {return;}
|
|
49
49
|
|
|
50
50
|
const allTools = [...state.toolMetadata.values()].flat().map(m => m.name);
|
|
51
51
|
|
|
@@ -119,7 +119,7 @@ export async function authenticateServer(
|
|
|
119
119
|
config: McpConfig,
|
|
120
120
|
ctx: ExtensionContext
|
|
121
121
|
): Promise<void> {
|
|
122
|
-
if (!ctx.hasUI) return;
|
|
122
|
+
if (!ctx.hasUI) {return;}
|
|
123
123
|
|
|
124
124
|
const definition = config.mcpServers[serverName];
|
|
125
125
|
if (!definition) {
|
|
@@ -172,7 +172,7 @@ export async function openMcpPanel(
|
|
|
172
172
|
const provenanceMap = getServerProvenance(pi.getFlag("mcp-config") as string | undefined ?? configOverridePath);
|
|
173
173
|
|
|
174
174
|
const callbacks: McpPanelCallbacks = {
|
|
175
|
-
reconnect: async
|
|
175
|
+
reconnect: async(serverName: string) => {
|
|
176
176
|
return lazyConnect(state, serverName);
|
|
177
177
|
},
|
|
178
178
|
getConnectionStatus: (serverName: string) => {
|
|
@@ -181,8 +181,8 @@ export async function openMcpPanel(
|
|
|
181
181
|
return "needs-auth";
|
|
182
182
|
}
|
|
183
183
|
const connection = state.manager.getConnection(serverName);
|
|
184
|
-
if (connection?.status === "connected") return "connected";
|
|
185
|
-
if (getFailureAgeSeconds(state, serverName) !== null) return "failed";
|
|
184
|
+
if (connection?.status === "connected") {return "connected";}
|
|
185
|
+
if (getFailureAgeSeconds(state, serverName) !== null) {return "failed";}
|
|
186
186
|
return "idle";
|
|
187
187
|
},
|
|
188
188
|
refreshCacheAfterReconnect: (serverName: string) => {
|
|
@@ -37,13 +37,13 @@ export function loadMcpConfig(overridePath?: string): McpConfig {
|
|
|
37
37
|
if (config.imports?.length) {
|
|
38
38
|
for (const importKind of config.imports) {
|
|
39
39
|
const importPath = IMPORT_PATHS[importKind];
|
|
40
|
-
if (!importPath) continue;
|
|
40
|
+
if (!importPath) {continue;}
|
|
41
41
|
|
|
42
42
|
const fullPath = importPath.startsWith(".")
|
|
43
43
|
? resolve(process.cwd(), importPath)
|
|
44
44
|
: importPath;
|
|
45
45
|
|
|
46
|
-
if (!existsSync(fullPath)) continue;
|
|
46
|
+
if (!existsSync(fullPath)) {continue;}
|
|
47
47
|
|
|
48
48
|
try {
|
|
49
49
|
const imported = JSON.parse(readFileSync(fullPath, "utf-8"));
|
|
@@ -102,7 +102,7 @@ function validateConfig(raw: unknown): McpConfig {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
function extractServers(config: unknown, kind: ImportKind): Record<string, ServerEntry> {
|
|
105
|
-
if (!config || typeof config !== "object") return {};
|
|
105
|
+
if (!config || typeof config !== "object") {return {};}
|
|
106
106
|
|
|
107
107
|
const obj = config as Record<string, unknown>;
|
|
108
108
|
|
|
@@ -146,11 +146,11 @@ export function getServerProvenance(overridePath?: string): Map<string, ServerPr
|
|
|
146
146
|
if (userConfig.imports?.length) {
|
|
147
147
|
for (const importKind of userConfig.imports) {
|
|
148
148
|
const importPath = IMPORT_PATHS[importKind];
|
|
149
|
-
if (!importPath) continue;
|
|
149
|
+
if (!importPath) {continue;}
|
|
150
150
|
const fullPath = importPath.startsWith(".")
|
|
151
151
|
? resolve(process.cwd(), importPath)
|
|
152
152
|
: importPath;
|
|
153
|
-
if (!existsSync(fullPath)) continue;
|
|
153
|
+
if (!existsSync(fullPath)) {continue;}
|
|
154
154
|
try {
|
|
155
155
|
const imported = JSON.parse(readFileSync(fullPath, "utf-8"));
|
|
156
156
|
const servers = extractServers(imported, importKind);
|
|
@@ -185,11 +185,11 @@ export function writeDirectToolsConfig(
|
|
|
185
185
|
|
|
186
186
|
for (const [serverName, value] of changes) {
|
|
187
187
|
const prov = provenance.get(serverName);
|
|
188
|
-
if (!prov) continue;
|
|
188
|
+
if (!prov) {continue;}
|
|
189
189
|
|
|
190
190
|
const targetPath = prov.path;
|
|
191
191
|
|
|
192
|
-
if (!byPath.has(targetPath)) byPath.set(targetPath, []);
|
|
192
|
+
if (!byPath.has(targetPath)) {byPath.set(targetPath, []);}
|
|
193
193
|
byPath.get(targetPath)!.push({ name: serverName, value, prov });
|
|
194
194
|
}
|
|
195
195
|
|
|
@@ -200,10 +200,10 @@ export function writeDirectToolsConfig(
|
|
|
200
200
|
raw = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
201
201
|
} catch {}
|
|
202
202
|
}
|
|
203
|
-
if (!raw || typeof raw !== "object") raw = {};
|
|
203
|
+
if (!raw || typeof raw !== "object") {raw = {};}
|
|
204
204
|
|
|
205
205
|
const servers = (raw.mcpServers ?? raw["mcp-servers"] ?? {}) as Record<string, ServerEntry>;
|
|
206
|
-
if (typeof servers !== "object" || Array.isArray(servers)) continue;
|
|
206
|
+
if (typeof servers !== "object" || Array.isArray(servers)) {continue;}
|
|
207
207
|
|
|
208
208
|
for (const { name, value, prov } of entries) {
|
|
209
209
|
if (prov.kind === "import") {
|
|
@@ -13,9 +13,9 @@ export class ConsentManager {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
requiresPrompt(serverName: string): boolean {
|
|
16
|
-
if (this.mode === "never") return false;
|
|
17
|
-
if (this.deniedServers.has(serverName)) return true;
|
|
18
|
-
if (this.mode === "always") return true;
|
|
16
|
+
if (this.mode === "never") {return false;}
|
|
17
|
+
if (this.deniedServers.has(serverName)) {return true;}
|
|
18
|
+
if (this.mode === "always") {return true;}
|
|
19
19
|
return !this.approvedServers.has(serverName);
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -38,7 +38,7 @@ export class ConsentManager {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
ensureApproved(serverName: string): void {
|
|
41
|
-
if (this.mode === "never") return;
|
|
41
|
+
if (this.mode === "never") {return;}
|
|
42
42
|
if (this.deniedServers.has(serverName)) {
|
|
43
43
|
throw new ConsentError(serverName, { denied: true });
|
|
44
44
|
}
|
|
@@ -19,7 +19,7 @@ export function resolveDirectTools(
|
|
|
19
19
|
envOverride?: string[],
|
|
20
20
|
): DirectToolSpec[] {
|
|
21
21
|
const specs: DirectToolSpec[] = [];
|
|
22
|
-
if (!cache) return specs;
|
|
22
|
+
if (!cache) {return specs;}
|
|
23
23
|
|
|
24
24
|
const seenNames = new Set<string>();
|
|
25
25
|
|
|
@@ -31,7 +31,7 @@ export function resolveDirectTools(
|
|
|
31
31
|
if (item.includes("/")) {
|
|
32
32
|
const [server, tool] = item.split("/", 2);
|
|
33
33
|
if (server && tool) {
|
|
34
|
-
if (!envTools.has(server)) envTools.set(server, new Set());
|
|
34
|
+
if (!envTools.has(server)) {envTools.set(server, new Set());}
|
|
35
35
|
envTools.get(server)!.add(tool);
|
|
36
36
|
} else if (server) {
|
|
37
37
|
envServers.add(server);
|
|
@@ -46,7 +46,7 @@ export function resolveDirectTools(
|
|
|
46
46
|
|
|
47
47
|
for (const [serverName, definition] of Object.entries(config.mcpServers)) {
|
|
48
48
|
const serverCache = cache.servers[serverName];
|
|
49
|
-
if (!serverCache || !isServerCacheValid(serverCache, definition)) continue;
|
|
49
|
+
if (!serverCache || !isServerCacheValid(serverCache, definition)) {continue;}
|
|
50
50
|
|
|
51
51
|
let toolFilter: true | string[] | false = false;
|
|
52
52
|
|
|
@@ -64,10 +64,10 @@ export function resolveDirectTools(
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
if (!toolFilter) continue;
|
|
67
|
+
if (!toolFilter) {continue;}
|
|
68
68
|
|
|
69
69
|
for (const tool of serverCache.tools ?? []) {
|
|
70
|
-
if (toolFilter !== true && !toolFilter.includes(tool.name)) continue;
|
|
70
|
+
if (toolFilter !== true && !toolFilter.includes(tool.name)) {continue;}
|
|
71
71
|
const prefixedName = formatToolName(tool.name, serverName, prefix);
|
|
72
72
|
if (BUILTIN_NAMES.has(prefixedName)) {
|
|
73
73
|
console.warn(`MCP: skipping direct tool "${prefixedName}" (collides with builtin)`);
|
|
@@ -92,7 +92,7 @@ export function resolveDirectTools(
|
|
|
92
92
|
if (definition.exposeResources !== false) {
|
|
93
93
|
for (const resource of serverCache.resources ?? []) {
|
|
94
94
|
const baseName = `get_${resourceNameToToolName(resource.name)}`;
|
|
95
|
-
if (toolFilter !== true && !toolFilter.includes(baseName)) continue;
|
|
95
|
+
if (toolFilter !== true && !toolFilter.includes(baseName)) {continue;}
|
|
96
96
|
const prefixedName = formatToolName(baseName, serverName, prefix);
|
|
97
97
|
if (BUILTIN_NAMES.has(prefixedName)) {
|
|
98
98
|
console.warn(`MCP: skipping direct resource tool "${prefixedName}" (collides with builtin)`);
|
|
@@ -142,7 +142,7 @@ export function buildProxyDescription(
|
|
|
142
142
|
const toolCount = entry?.tools?.length ?? 0;
|
|
143
143
|
const resourceCount = definition?.exposeResources !== false ? (entry?.resources?.length ?? 0) : 0;
|
|
144
144
|
const totalItems = toolCount + resourceCount;
|
|
145
|
-
if (totalItems === 0) continue;
|
|
145
|
+
if (totalItems === 0) {continue;}
|
|
146
146
|
const directCount = directByServer.get(serverName) ?? 0;
|
|
147
147
|
const proxyCount = totalItems - directCount;
|
|
148
148
|
if (proxyCount > 0) {
|
|
@@ -233,12 +233,12 @@ export function createDirectToolExecutor(
|
|
|
233
233
|
const hasUi = !!spec.uiResourceUri;
|
|
234
234
|
uiSession = hasUi
|
|
235
235
|
? await maybeStartUiSession(state, {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
236
|
+
serverName: spec.serverName,
|
|
237
|
+
toolName: spec.originalName,
|
|
238
|
+
toolArgs: params ?? {},
|
|
239
|
+
uiResourceUri: spec.uiResourceUri!,
|
|
240
|
+
streamMode: spec.uiStreamMode,
|
|
241
|
+
})
|
|
242
242
|
: null;
|
|
243
243
|
|
|
244
244
|
const resultPromise = connection.client.callTool({
|
|
@@ -8,7 +8,7 @@ let glimpseAvailable: boolean | null = null;
|
|
|
8
8
|
let resolvedBinaryPath: string | null = null;
|
|
9
9
|
|
|
10
10
|
export function isGlimpseAvailable(): boolean {
|
|
11
|
-
if (glimpseAvailable !== null) return glimpseAvailable;
|
|
11
|
+
if (glimpseAvailable !== null) {return glimpseAvailable;}
|
|
12
12
|
|
|
13
13
|
if (platform() !== "darwin") {
|
|
14
14
|
glimpseAvailable = false;
|
|
@@ -30,14 +30,14 @@ function getGlimpseBinaryPath(): string | null {
|
|
|
30
30
|
const require = createRequire(import.meta.url);
|
|
31
31
|
const glimpseuiPath = require.resolve("glimpseui");
|
|
32
32
|
const binaryPath = join(dirname(glimpseuiPath), "glimpse");
|
|
33
|
-
if (existsSync(binaryPath)) return binaryPath;
|
|
33
|
+
if (existsSync(binaryPath)) {return binaryPath;}
|
|
34
34
|
} catch {}
|
|
35
35
|
|
|
36
36
|
// Global npm install
|
|
37
37
|
try {
|
|
38
38
|
const globalRoot = execFileSync("npm", ["root", "-g"], { encoding: "utf-8" }).trim();
|
|
39
39
|
const binaryPath = join(globalRoot, "glimpseui", "src", "glimpse");
|
|
40
|
-
if (existsSync(binaryPath)) return binaryPath;
|
|
40
|
+
if (existsSync(binaryPath)) {return binaryPath;}
|
|
41
41
|
} catch {}
|
|
42
42
|
|
|
43
43
|
return null;
|
|
@@ -65,14 +65,14 @@ export async function openGlimpseWindow(
|
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
win.on("closed", () => {
|
|
68
|
-
if (!active) return;
|
|
68
|
+
if (!active) {return;}
|
|
69
69
|
active = false;
|
|
70
70
|
options.onClosed();
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
return {
|
|
74
74
|
close: () => {
|
|
75
|
-
if (!active) return;
|
|
75
|
+
if (!active) {return;}
|
|
76
76
|
active = false;
|
|
77
77
|
win.close();
|
|
78
78
|
},
|
|
@@ -357,7 +357,7 @@ export function buildHostHtmlTemplate(input: HostHtmlTemplateInput): string {
|
|
|
357
357
|
}
|
|
358
358
|
|
|
359
359
|
export function buildCspMetaContent(csp: UiResourceCsp | undefined): string | undefined {
|
|
360
|
-
if (!csp) return undefined;
|
|
360
|
+
if (!csp) {return undefined;}
|
|
361
361
|
|
|
362
362
|
const directives: string[] = [];
|
|
363
363
|
directives.push("default-src 'none'");
|
|
@@ -372,27 +372,27 @@ export function buildCspMetaContent(csp: UiResourceCsp | undefined): string | un
|
|
|
372
372
|
const workerSrc = toDirective("worker-src", csp.workerDomains);
|
|
373
373
|
const baseUri = toDirective("base-uri", csp.baseUriDomains);
|
|
374
374
|
|
|
375
|
-
if (scriptSrc) directives.push(scriptSrc);
|
|
376
|
-
if (styleSrc) directives.push(styleSrc);
|
|
377
|
-
if (fontSrc) directives.push(fontSrc);
|
|
378
|
-
if (imgSrc) directives.push(imgSrc);
|
|
379
|
-
if (mediaSrc) directives.push(mediaSrc);
|
|
380
|
-
if (connectSrc) directives.push(connectSrc);
|
|
381
|
-
if (frameSrc) directives.push(frameSrc);
|
|
382
|
-
if (workerSrc) directives.push(workerSrc);
|
|
383
|
-
if (baseUri) directives.push(baseUri);
|
|
375
|
+
if (scriptSrc) {directives.push(scriptSrc);}
|
|
376
|
+
if (styleSrc) {directives.push(styleSrc);}
|
|
377
|
+
if (fontSrc) {directives.push(fontSrc);}
|
|
378
|
+
if (imgSrc) {directives.push(imgSrc);}
|
|
379
|
+
if (mediaSrc) {directives.push(mediaSrc);}
|
|
380
|
+
if (connectSrc) {directives.push(connectSrc);}
|
|
381
|
+
if (frameSrc) {directives.push(frameSrc);}
|
|
382
|
+
if (workerSrc) {directives.push(workerSrc);}
|
|
383
|
+
if (baseUri) {directives.push(baseUri);}
|
|
384
384
|
|
|
385
385
|
return directives.join("; ");
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
function toDirective(name: string, domains: string[] | undefined): string | null {
|
|
389
|
-
if (!domains || domains.length === 0) return null;
|
|
389
|
+
if (!domains || domains.length === 0) {return null;}
|
|
390
390
|
return `${name} ${domains.join(" ")}`;
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
export function applyCspMeta(html: string, cspContent: string | undefined): string {
|
|
394
|
-
if (!cspContent) return html;
|
|
395
|
-
if (/http-equiv=["']Content-Security-Policy["']/i.test(html)) return html;
|
|
394
|
+
if (!cspContent) {return html;}
|
|
395
|
+
if (/http-equiv=["']Content-Security-Policy["']/i.test(html)) {return html;}
|
|
396
396
|
const metaTag = `<meta http-equiv="Content-Security-Policy" content="${escapeHtmlAttribute(cspContent)}">`;
|
|
397
397
|
if (/<head[^>]*>/i.test(html)) {
|
|
398
398
|
return html.replace(/<head[^>]*>/i, (match) => `${match}\n${metaTag}`);
|
|
@@ -22,11 +22,11 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
22
22
|
const directSpecs = envRaw === "__none__"
|
|
23
23
|
? []
|
|
24
24
|
: resolveDirectTools(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
earlyConfig,
|
|
26
|
+
earlyCache,
|
|
27
|
+
prefix,
|
|
28
|
+
envRaw?.split(",").map(s => s.trim()).filter(Boolean),
|
|
29
|
+
);
|
|
30
30
|
|
|
31
31
|
for (const spec of directSpecs) {
|
|
32
32
|
pi.registerTool({
|
|
@@ -45,7 +45,7 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
45
45
|
type: "string",
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
pi.on("session_start", async
|
|
48
|
+
pi.on("session_start", async(_event, ctx) => {
|
|
49
49
|
initPromise = initializeMcp(pi, ctx);
|
|
50
50
|
|
|
51
51
|
initPromise.then(s => {
|
|
@@ -58,7 +58,7 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
pi.on("session_shutdown", async
|
|
61
|
+
pi.on("session_shutdown", async() => {
|
|
62
62
|
if (initPromise) {
|
|
63
63
|
try {
|
|
64
64
|
state = await initPromise;
|
|
@@ -80,17 +80,17 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
80
80
|
|
|
81
81
|
pi.registerCommand("mcp", {
|
|
82
82
|
description: "Show MCP server status",
|
|
83
|
-
handler: async
|
|
83
|
+
handler: async(args, ctx) => {
|
|
84
84
|
if (!state && initPromise) {
|
|
85
85
|
try {
|
|
86
86
|
state = await initPromise;
|
|
87
87
|
} catch {
|
|
88
|
-
if (ctx.hasUI) ctx.ui.notify("MCP initialization failed", "error");
|
|
88
|
+
if (ctx.hasUI) {ctx.ui.notify("MCP initialization failed", "error");}
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
if (!state) {
|
|
93
|
-
if (ctx.hasUI) ctx.ui.notify("MCP not initialized", "error");
|
|
93
|
+
if (ctx.hasUI) {ctx.ui.notify("MCP not initialized", "error");}
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -120,10 +120,10 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
120
120
|
|
|
121
121
|
pi.registerCommand("mcp-auth", {
|
|
122
122
|
description: "Authenticate with an MCP server (OAuth)",
|
|
123
|
-
handler: async
|
|
123
|
+
handler: async(args, ctx) => {
|
|
124
124
|
const serverName = args?.trim();
|
|
125
125
|
if (!serverName) {
|
|
126
|
-
if (ctx.hasUI) ctx.ui.notify("Usage: /mcp-auth <server-name>", "error");
|
|
126
|
+
if (ctx.hasUI) {ctx.ui.notify("Usage: /mcp-auth <server-name>", "error");}
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -131,12 +131,12 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
131
131
|
try {
|
|
132
132
|
state = await initPromise;
|
|
133
133
|
} catch {
|
|
134
|
-
if (ctx.hasUI) ctx.ui.notify("MCP initialization failed", "error");
|
|
134
|
+
if (ctx.hasUI) {ctx.ui.notify("MCP initialization failed", "error");}
|
|
135
135
|
return;
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
if (!state) {
|
|
139
|
-
if (ctx.hasUI) ctx.ui.notify("MCP not initialized", "error");
|
|
139
|
+
if (ctx.hasUI) {ctx.ui.notify("MCP not initialized", "error");}
|
|
140
140
|
return;
|
|
141
141
|
}
|
|
142
142
|
|
|
@@ -97,15 +97,15 @@ export async function initializeMcp(
|
|
|
97
97
|
const startupServers = bootstrapAll
|
|
98
98
|
? serverEntries
|
|
99
99
|
: serverEntries.filter(([, definition]) => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
const mode = definition.lifecycle ?? "lazy";
|
|
101
|
+
return mode === "keep-alive" || mode === "eager";
|
|
102
|
+
});
|
|
103
103
|
|
|
104
104
|
if (ctx.hasUI && startupServers.length > 0) {
|
|
105
105
|
ctx.ui.setStatus("mcp", `MCP: connecting to ${startupServers.length} servers...`);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
const results = await parallelLimit(startupServers, 10, async
|
|
108
|
+
const results = await parallelLimit(startupServers, 10, async([name, definition]) => {
|
|
109
109
|
try {
|
|
110
110
|
const connection = await manager.connect(name, definition);
|
|
111
111
|
return { name, definition, connection, error: null };
|
|
@@ -154,7 +154,7 @@ export async function initializeMcp(
|
|
|
154
154
|
const hasDirect = definition.directTools !== undefined
|
|
155
155
|
? !!definition.directTools
|
|
156
156
|
: !!config.settings?.directTools;
|
|
157
|
-
if (!hasDirect) continue;
|
|
157
|
+
if (!hasDirect) {continue;}
|
|
158
158
|
const entry = currentCache?.servers?.[name];
|
|
159
159
|
if (!entry || !isServerCacheValid(entry, definition)) {
|
|
160
160
|
missingCacheServers.push(name);
|
|
@@ -165,7 +165,7 @@ export async function initializeMcp(
|
|
|
165
165
|
const bootstrapResults = await parallelLimit(
|
|
166
166
|
missingCacheServers.filter(name => !results.some(r => r.name === name && r.connection)),
|
|
167
167
|
10,
|
|
168
|
-
async
|
|
168
|
+
async(name) => {
|
|
169
169
|
const definition = config.mcpServers[name];
|
|
170
170
|
try {
|
|
171
171
|
const connection = await manager.connect(name, definition);
|
|
@@ -205,10 +205,10 @@ export async function initializeMcp(
|
|
|
205
205
|
|
|
206
206
|
export function updateServerMetadata(state: McpExtensionState, serverName: string): void {
|
|
207
207
|
const connection = state.manager.getConnection(serverName);
|
|
208
|
-
if (!connection || connection.status !== "connected") return;
|
|
208
|
+
if (!connection || connection.status !== "connected") {return;}
|
|
209
209
|
|
|
210
210
|
const definition = state.config.mcpServers[serverName];
|
|
211
|
-
if (!definition) return;
|
|
211
|
+
if (!definition) {return;}
|
|
212
212
|
|
|
213
213
|
const prefix = state.config.settings?.toolPrefix ?? "server";
|
|
214
214
|
|
|
@@ -218,10 +218,10 @@ export function updateServerMetadata(state: McpExtensionState, serverName: strin
|
|
|
218
218
|
|
|
219
219
|
export function updateMetadataCache(state: McpExtensionState, serverName: string): void {
|
|
220
220
|
const connection = state.manager.getConnection(serverName);
|
|
221
|
-
if (!connection || connection.status !== "connected") return;
|
|
221
|
+
if (!connection || connection.status !== "connected") {return;}
|
|
222
222
|
|
|
223
223
|
const definition = state.config.mcpServers[serverName];
|
|
224
|
-
if (!definition) return;
|
|
224
|
+
if (!definition) {return;}
|
|
225
225
|
|
|
226
226
|
const configHash = computeServerHash(definition);
|
|
227
227
|
const existing = loadMetadataCache();
|
|
@@ -259,7 +259,7 @@ export function flushMetadataCache(state: McpExtensionState): void {
|
|
|
259
259
|
|
|
260
260
|
export function updateStatusBar(state: McpExtensionState): void {
|
|
261
261
|
const ui = state.ui;
|
|
262
|
-
if (!ui) return;
|
|
262
|
+
if (!ui) {return;}
|
|
263
263
|
const total = Object.keys(state.config.mcpServers).length;
|
|
264
264
|
if (total === 0) {
|
|
265
265
|
ui.setStatus("mcp", "");
|
|
@@ -271,9 +271,9 @@ export function updateStatusBar(state: McpExtensionState): void {
|
|
|
271
271
|
|
|
272
272
|
export function getFailureAgeSeconds(state: McpExtensionState, serverName: string): number | null {
|
|
273
273
|
const failedAt = state.failureTracker.get(serverName);
|
|
274
|
-
if (!failedAt) return null;
|
|
274
|
+
if (!failedAt) {return null;}
|
|
275
275
|
const ageMs = Date.now() - failedAt;
|
|
276
|
-
if (ageMs > FAILURE_BACKOFF_MS) return null;
|
|
276
|
+
if (ageMs > FAILURE_BACKOFF_MS) {return null;}
|
|
277
277
|
return Math.round(ageMs / 1000);
|
|
278
278
|
}
|
|
279
279
|
|
|
@@ -285,10 +285,10 @@ export async function lazyConnect(state: McpExtensionState, serverName: string):
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
const failedAgo = getFailureAgeSeconds(state, serverName);
|
|
288
|
-
if (failedAgo !== null) return false;
|
|
288
|
+
if (failedAgo !== null) {return false;}
|
|
289
289
|
|
|
290
290
|
const definition = state.config.mcpServers[serverName];
|
|
291
|
-
if (!definition) return false;
|
|
291
|
+
if (!definition) {return false;}
|
|
292
292
|
|
|
293
293
|
try {
|
|
294
294
|
if (state.ui) {
|
|
@@ -312,8 +312,8 @@ function getEffectiveIdleTimeoutMinutes(state: McpExtensionState, serverName: st
|
|
|
312
312
|
if (!definition) {
|
|
313
313
|
return typeof state.config.settings?.idleTimeout === "number" ? state.config.settings.idleTimeout : 10;
|
|
314
314
|
}
|
|
315
|
-
if (typeof definition.idleTimeout === "number") return definition.idleTimeout;
|
|
315
|
+
if (typeof definition.idleTimeout === "number") {return definition.idleTimeout;}
|
|
316
316
|
const mode = definition.lifecycle ?? "lazy";
|
|
317
|
-
if (mode === "eager") return 0;
|
|
317
|
+
if (mode === "eager") {return 0;}
|
|
318
318
|
return typeof state.config.settings?.idleTimeout === "number" ? state.config.settings.idleTimeout : 10;
|
|
319
319
|
}
|
|
@@ -69,7 +69,7 @@ export class McpLifecycleManager {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
for (const [name] of this.allServers) {
|
|
72
|
-
if (this.keepAliveServers.has(name)) continue;
|
|
72
|
+
if (this.keepAliveServers.has(name)) {continue;}
|
|
73
73
|
const timeout = this.getIdleTimeout(name);
|
|
74
74
|
if (timeout > 0 && this.manager.isIdle(name, timeout)) {
|
|
75
75
|
await this.manager.close(name);
|
|
@@ -80,7 +80,7 @@ export class McpLifecycleManager {
|
|
|
80
80
|
|
|
81
81
|
private getIdleTimeout(name: string): number {
|
|
82
82
|
const perServer = this.serverSettings.get(name)?.idleTimeout;
|
|
83
|
-
if (perServer !== undefined) return perServer * 60 * 1000;
|
|
83
|
+
if (perServer !== undefined) {return perServer * 60 * 1000;}
|
|
84
84
|
return this.globalIdleTimeout;
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -63,7 +63,7 @@ class Logger {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
private emit(level: LogLevel, message: string, context?: LogContext, error?: Error): void {
|
|
66
|
-
if (!this.shouldLog(level)) return;
|
|
66
|
+
if (!this.shouldLog(level)) {return;}
|
|
67
67
|
|
|
68
68
|
const entry: LogEntry = {
|
|
69
69
|
level,
|
|
@@ -150,7 +150,7 @@ class ChildLogger {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
function formatContext(context?: LogContext): string {
|
|
153
|
-
if (!context || Object.keys(context).length === 0) return "";
|
|
153
|
+
if (!context || Object.keys(context).length === 0) {return "";}
|
|
154
154
|
const parts: string[] = [];
|
|
155
155
|
for (const [key, value] of Object.entries(context)) {
|
|
156
156
|
if (value !== undefined && value !== null) {
|
|
@@ -30,7 +30,7 @@ const DEFAULT_THEME: PanelTheme = {
|
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
function fg(code: string, text: string): string {
|
|
33
|
-
if (!code) return text;
|
|
33
|
+
if (!code) {return text;}
|
|
34
34
|
return `\x1b[${code}m${text}\x1b[0m`;
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -56,7 +56,7 @@ function rainbowProgress(filled: number, total: number): string {
|
|
|
56
56
|
function fuzzyScore(query: string, text: string): number {
|
|
57
57
|
const lq = query.toLowerCase();
|
|
58
58
|
const lt = text.toLowerCase();
|
|
59
|
-
if (lt.includes(lq)) return 100 + (lq.length / lt.length) * 50;
|
|
59
|
+
if (lt.includes(lq)) {return 100 + (lq.length / lt.length) * 50;}
|
|
60
60
|
let score = 0;
|
|
61
61
|
let qi = 0;
|
|
62
62
|
let consecutive = 0;
|
|
@@ -191,7 +191,7 @@ class McpPanel {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
private resetInactivityTimeout(): void {
|
|
194
|
-
if (this.inactivityTimeout) clearTimeout(this.inactivityTimeout);
|
|
194
|
+
if (this.inactivityTimeout) {clearTimeout(this.inactivityTimeout);}
|
|
195
195
|
this.inactivityTimeout = setTimeout(() => {
|
|
196
196
|
this.cleanup();
|
|
197
197
|
this.done({ cancelled: true, changes: new Map() });
|
|
@@ -219,11 +219,11 @@ class McpPanel {
|
|
|
219
219
|
if (query) {
|
|
220
220
|
const score = mode === "name"
|
|
221
221
|
? Math.max(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
222
|
+
fuzzyScore(query, tool.name),
|
|
223
|
+
fuzzyScore(query, server.name) * 0.6,
|
|
224
|
+
)
|
|
225
225
|
: fuzzyScore(query, tool.description);
|
|
226
|
-
if (score === 0) continue;
|
|
226
|
+
if (score === 0) {continue;}
|
|
227
227
|
}
|
|
228
228
|
this.visibleItems.push({ type: "tool", serverIndex: si, toolIndex: ti });
|
|
229
229
|
}
|
|
@@ -250,7 +250,7 @@ class McpPanel {
|
|
|
250
250
|
const changes = new Map<string, true | string[] | false>();
|
|
251
251
|
for (const server of this.servers) {
|
|
252
252
|
const changed = server.tools.some((t) => t.isDirect !== t.wasDirect);
|
|
253
|
-
if (!changed) continue;
|
|
253
|
+
if (!changed) {continue;}
|
|
254
254
|
const directTools = server.tools.filter((t) => t.isDirect);
|
|
255
255
|
if (directTools.length === server.tools.length && server.tools.length > 0) {
|
|
256
256
|
changes.set(server.name, true);
|
|
@@ -308,7 +308,7 @@ class McpPanel {
|
|
|
308
308
|
if (matchesKey(data, "space")) {
|
|
309
309
|
// Toggle even while in desc search
|
|
310
310
|
const item = this.visibleItems[this.cursorIndex];
|
|
311
|
-
if (item) this.toggleItem(item);
|
|
311
|
+
if (item) {this.toggleItem(item);}
|
|
312
312
|
return;
|
|
313
313
|
}
|
|
314
314
|
if (data.length === 1 && data.charCodeAt(0) >= 32) {
|
|
@@ -342,13 +342,13 @@ class McpPanel {
|
|
|
342
342
|
|
|
343
343
|
if (matchesKey(data, "space")) {
|
|
344
344
|
const item = this.visibleItems[this.cursorIndex];
|
|
345
|
-
if (item) this.toggleItem(item);
|
|
345
|
+
if (item) {this.toggleItem(item);}
|
|
346
346
|
return;
|
|
347
347
|
}
|
|
348
348
|
|
|
349
349
|
if (matchesKey(data, "return")) {
|
|
350
350
|
const item = this.visibleItems[this.cursorIndex];
|
|
351
|
-
if (!item) return;
|
|
351
|
+
if (!item) {return;}
|
|
352
352
|
const server = this.servers[item.serverIndex];
|
|
353
353
|
if (item.type === "server") {
|
|
354
354
|
if (server.connectionStatus === "needs-auth") {
|
|
@@ -371,9 +371,9 @@ class McpPanel {
|
|
|
371
371
|
|
|
372
372
|
if (matchesKey(data, "ctrl+r")) {
|
|
373
373
|
const item = this.visibleItems[this.cursorIndex];
|
|
374
|
-
if (!item) return;
|
|
374
|
+
if (!item) {return;}
|
|
375
375
|
const server = this.servers[item.serverIndex];
|
|
376
|
-
if (server.connectionStatus === "connecting") return;
|
|
376
|
+
if (server.connectionStatus === "connecting") {return;}
|
|
377
377
|
server.connectionStatus = "connecting";
|
|
378
378
|
this.callbacks.reconnect(server.name).then(() => {
|
|
379
379
|
server.connectionStatus = this.callbacks.getConnectionStatus(server.name);
|
|
@@ -426,7 +426,7 @@ class McpPanel {
|
|
|
426
426
|
if (server.source === "import" && newState) {
|
|
427
427
|
this.importNotice = `Imported from ${server.importKind ?? "external"} — will copy to user config on save`;
|
|
428
428
|
}
|
|
429
|
-
for (const t of server.tools) t.isDirect = newState;
|
|
429
|
+
for (const t of server.tools) {t.isDirect = newState;}
|
|
430
430
|
} else if (item.toolIndex !== undefined) {
|
|
431
431
|
const tool = server.tools[item.toolIndex];
|
|
432
432
|
tool.isDirect = !tool.isDirect;
|
|
@@ -467,13 +467,13 @@ class McpPanel {
|
|
|
467
467
|
}
|
|
468
468
|
|
|
469
469
|
private moveCursor(delta: number): void {
|
|
470
|
-
if (this.visibleItems.length === 0) return;
|
|
470
|
+
if (this.visibleItems.length === 0) {return;}
|
|
471
471
|
this.cursorIndex = Math.max(0, Math.min(this.visibleItems.length - 1, this.cursorIndex + delta));
|
|
472
472
|
}
|
|
473
473
|
|
|
474
474
|
private rebuildServerTools(server: ServerState, entry: ServerCacheEntry): void {
|
|
475
475
|
const existingState = new Map<string, boolean>();
|
|
476
|
-
for (const t of server.tools) existingState.set(t.name, t.isDirect);
|
|
476
|
+
for (const t of server.tools) {existingState.set(t.name, t.isDirect);}
|
|
477
477
|
|
|
478
478
|
const newTools: ToolState[] = [];
|
|
479
479
|
for (const tool of entry.tools ?? []) {
|
|
@@ -633,7 +633,7 @@ class McpPanel {
|
|
|
633
633
|
curW += needed;
|
|
634
634
|
}
|
|
635
635
|
}
|
|
636
|
-
if (curLine) lines.push(row(fg(t.hint, curLine)));
|
|
636
|
+
if (curLine) {lines.push(row(fg(t.hint, curLine)));}
|
|
637
637
|
|
|
638
638
|
lines.push(fg(t.border, "╰" + "─".repeat(innerW) + "╯"));
|
|
639
639
|
|