Skip to content

Commit 8da8146

Browse files
committed
feat(cli, config, logger, main, file-read, symbol-resolver): remove logFile references and enhance error handling
1 parent 80813ed commit 8da8146

6 files changed

Lines changed: 30 additions & 68 deletions

File tree

src/cli.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,6 @@ export const cliOptions = {
6565
default: false,
6666
hidden: true,
6767
},
68-
logFile: {
69-
type: 'string',
70-
describe: '[LEGACY] Set logFile in .vscode/devtools.json instead.',
71-
hidden: true,
72-
},
7368
experimentalVision: {
7469
type: 'boolean',
7570
describe: 'Whether to enable vision tools.',

src/config.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const DEFAULT_CONFIG_TEMPLATE = `// VS Code DevTools MCP configuration (JSONC)
7777
"devDiagnostic": false,
7878
7979
// Write logs to a file (absolute or relative path).
80-
// "logFile": ".devtools/devtools-mcp.log",
80+
// Logs are written to stderr and appear in VS Code's MCP output channel.
8181
8282
// Run VS Code headless (Linux only).
8383
"headless": false,
@@ -134,9 +134,6 @@ export interface DevToolsConfig {
134134
/** Enable diagnostic tools (debug_evaluate) */
135135
devDiagnostic?: boolean;
136136

137-
/** Path to log file (relative to workspace or absolute) */
138-
logFile?: string;
139-
140137
/** Run VS Code headless (Linux only) */
141138
headless?: boolean;
142139

@@ -162,7 +159,6 @@ export interface ResolvedConfig {
162159
/** True when the extension dev path was explicitly supplied via CLI args. */
163160
explicitExtensionDevelopmentPath: boolean;
164161
devDiagnostic: boolean;
165-
logFile?: string;
166162
headless: boolean;
167163
experimentalVision: boolean;
168164
experimentalStructuredContent: boolean;
@@ -214,9 +210,6 @@ function coerceDevToolsConfig(value: unknown): DevToolsConfig {
214210
const devDiagnostic = readOptionalBoolean(value, 'devDiagnostic');
215211
if (typeof devDiagnostic === 'boolean') {config.devDiagnostic = devDiagnostic;}
216212

217-
const logFile = readOptionalString(value, 'logFile');
218-
if (logFile) {config.logFile = logFile;}
219-
220213
const headless = readOptionalBoolean(value, 'headless');
221214
if (typeof headless === 'boolean') {config.headless = headless;}
222215

@@ -393,7 +386,6 @@ export function loadConfig(cliArgs: {
393386
extensionBridgePath?: string;
394387
targetFolder?: string;
395388
devDiagnostic?: boolean;
396-
logFile?: string;
397389
headless?: boolean;
398390
experimentalVision?: boolean;
399391
experimentalStructuredContent?: boolean;
@@ -459,18 +451,12 @@ export function loadConfig(cliArgs: {
459451
extensionBridgePath = getDefaultExtensionPath();
460452
}
461453

462-
// Resolve log file path
463-
const logFile =
464-
cliArgs.logFile ??
465-
resolvePath(absoluteWorkspace, fileConfig.logFile);
466-
467454
return {
468455
hostWorkspace: getHostWorkspace(),
469456
workspaceFolder: absoluteWorkspace,
470457
extensionBridgePath,
471458
explicitExtensionDevelopmentPath,
472459
devDiagnostic: cliArgs.devDiagnostic ?? fileConfig.devDiagnostic ?? false,
473-
logFile,
474460
headless: cliArgs.headless ?? fileConfig.headless ?? false,
475461
experimentalVision:
476462
cliArgs.experimentalVision ?? fileConfig.experimentalVision ?? false,

src/logger.ts

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import fs from 'node:fs';
8-
97
import {debug} from './third_party/index.js';
108

119
const mcpDebugNamespace = 'mcp:log';
@@ -28,8 +26,7 @@ function formatLogChunks(chunks: unknown[]): string {
2826
return raw.replace(DEBUG_PREFIX_RE, '').replace(DEBUG_SUFFIX_RE, '');
2927
}
3028

31-
// Always enable the mcp:log namespace so output is visible.
32-
// By default, write to stderr in a clean timestamped format.
29+
// All logs go to stderr only.
3330
// stderr output appears in the host VS Code's MCP output channel
3431
// as "[server stderr]" entries, giving full visibility via read_output_channels.
3532
debug.enable(namespacesToEnable.join(','));
@@ -38,38 +35,4 @@ debug.log = function (...chunks: unknown[]) {
3835
process.stderr.write(`[${ts}] ${formatLogChunks(chunks)}\n`);
3936
};
4037

41-
export function saveLogsToFile(fileName: string): fs.WriteStream {
42-
// Re-enable (idempotent) and tee output to BOTH stderr and the log file.
43-
// stderr output appears in the host VS Code's MCP output channel;
44-
// the file provides a persistent on-disk record for post-mortem analysis.
45-
debug.enable(namespacesToEnable.join(','));
46-
47-
const logFile = fs.createWriteStream(fileName, {flags: 'a+'});
48-
debug.log = function (...chunks: unknown[]) {
49-
const ts = new Date().toISOString();
50-
const line = `[${ts}] ${formatLogChunks(chunks)}\n`;
51-
process.stderr.write(line);
52-
logFile.write(line);
53-
};
54-
logFile.on('error', function (error) {
55-
console.error(`Error when opening/writing to log file: ${error.message}`);
56-
logFile.end();
57-
process.exit(1);
58-
});
59-
return logFile;
60-
}
61-
62-
export function flushLogs(
63-
logFile: fs.WriteStream,
64-
timeoutMs = 2000,
65-
): Promise<void> {
66-
return new Promise((resolve, reject) => {
67-
const timeout = setTimeout(reject, timeoutMs);
68-
logFile.end(() => {
69-
clearTimeout(timeout);
70-
resolve();
71-
});
72-
});
73-
}
74-
7538
export const logger = debug(mcpDebugNamespace);

src/main.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {loadConfig, type ResolvedConfig} from './config.js';
1515
import {hasBuildChangedSinceWindowStart, isBuildStale, writeExtSourceFingerprint} from './extension-watcher.js';
1616
import {restartMcpServer, showHostNotification} from './host-pipe.js';
1717
import {loadIssueDescriptions} from './issue-descriptions.js';
18-
import {logger, saveLogsToFile} from './logger.js';
18+
import {logger} from './logger.js';
1919
import {McpResponse} from './McpResponse.js';
2020
import {
2121
consumeHotReloadMarker,
@@ -228,10 +228,9 @@ function formatProcessLedger(ledger: ProcessLedgerSummary): string {
228228
}
229229
}
230230

231-
// If nothing to report
231+
// If nothing to report, return empty string (no notification)
232232
if (ledger.orphaned.length === 0 && sessions.length === 0 && ledger.active.length === 0 && completed.length === 0) {
233-
parts.push('\n---');
234-
parts.push('\n📋 **No Copilot-managed processes running.**');
233+
return '';
235234
}
236235

237236
return parts.join('');
@@ -273,10 +272,6 @@ export const config: ResolvedConfig = loadConfig(cliArgs);
273272
// Legacy export for backwards compatibility
274273
export const args = cliArgs;
275274

276-
if (config.logFile) {
277-
saveLogsToFile(config.logFile);
278-
}
279-
280275
// ── MCP Server Hot-Reload Marker (detect post-restart) ───
281276
// Check this BEFORE initializing lifecycle service so we can pass the flag
282277
const _hotReloadBuildTime = consumeHotReloadMarker(mcpServerDir);
@@ -322,7 +317,7 @@ process.on('unhandledRejection', (reason, promise) => {
322317

323318
logger(`Starting VS Code DevTools MCP Server v${VERSION}`);
324319
logger(`Config: hostWorkspace=${config.hostWorkspace}, targetFolder=${config.workspaceFolder}`);
325-
logger(`Config: extensionBridgePath=${config.extensionBridgePath}, headless=${config.headless}, logFile=${config.logFile ?? '(none)'}`);
320+
logger(`Config: extensionBridgePath=${config.extensionBridgePath}, headless=${config.headless}`);
326321
const server = new McpServer(
327322
{
328323
name: 'vscode_devtools',
@@ -598,7 +593,9 @@ function registerTool(tool: ToolDefinition): void {
598593
if (!response.skipLedger) {
599594
const ledger = await getProcessLedger();
600595
const ledgerText = formatProcessLedger(ledger);
601-
content.push({type: 'text', text: ledgerText});
596+
if (ledgerText) {
597+
content.push({type: 'text', text: ledgerText});
598+
}
602599
}
603600

604601
return {content};

src/tools/file/file-read.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7+
import fs from 'node:fs';
78
import path from 'node:path';
89

910
import {
@@ -460,6 +461,20 @@ export const read = defineTool({
460461
handler: async (request, response) => {
461462
const {params} = request;
462463
const filePath = resolveFilePath(params.file);
464+
465+
if (!fs.existsSync(filePath)) {
466+
response.appendResponseLine(
467+
`**Error:** File not found: \`${filePath}\``,
468+
);
469+
if (!path.isAbsolute(params.file)) {
470+
response.appendResponseLine(
471+
`The relative path \`${params.file}\` was resolved against the workspace root. ` +
472+
'Use an absolute path or a path relative to the workspace root.',
473+
);
474+
}
475+
return;
476+
}
477+
463478
const skeleton = params.skeleton ?? false;
464479
const recursive = params.recursive ?? false;
465480

src/tools/file/symbol-resolver.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@
88
* Minimum shape required for symbol tree navigation.
99
* Both FileSymbol and UnifiedFileSymbol satisfy this.
1010
*/
11+
export interface SymbolLikeRange {
12+
startLine: number;
13+
endLine: number;
14+
}
15+
1116
export interface SymbolLike {
1217
name: string;
1318
kind: string;
19+
range: SymbolLikeRange;
1420
children: SymbolLike[];
1521
}
1622

0 commit comments

Comments
 (0)