vscode-terminal-mcp 0.1.3 → 0.1.5

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/dist/extension.js CHANGED
@@ -5389,6 +5389,13 @@ var zodToJsonSchema = (schema, options) => {
5389
5389
  };
5390
5390
 
5391
5391
  // src/mcp/tools/schemas.ts
5392
+ var coerceBoolean = external_exports.preprocess(
5393
+ (val) => {
5394
+ if (typeof val === "string") return val.toLowerCase() === "true";
5395
+ return val;
5396
+ },
5397
+ external_exports.boolean()
5398
+ );
5392
5399
  var terminalCreateSchema = external_exports.object({
5393
5400
  name: external_exports.string().min(1).describe("Display name for the terminal tab"),
5394
5401
  cwd: external_exports.string().optional().describe("Working directory for the terminal"),
@@ -5400,7 +5407,7 @@ var terminalExecuteSchema = external_exports.object({
5400
5407
  sessionId: external_exports.string().min(1).describe("Session ID of the target terminal"),
5401
5408
  command: external_exports.string().min(1).describe("Command to execute"),
5402
5409
  timeoutMs: external_exports.coerce.number().min(1e3).max(3e5).optional().default(3e4).describe("Timeout in milliseconds (default: 30000, max: 300000)"),
5403
- waitForCompletion: external_exports.coerce.boolean().optional().default(true).describe("Wait for command to complete before returning (default: true)")
5410
+ waitForCompletion: coerceBoolean.optional().default(true).describe("Wait for command to complete before returning (default: true)")
5404
5411
  });
5405
5412
  var terminalReadOutputSchema = external_exports.object({
5406
5413
  sessionId: external_exports.string().min(1).describe("Session ID of the target terminal"),
@@ -5421,7 +5428,7 @@ var terminalRunSchema = external_exports.object({
5421
5428
  shell: external_exports.string().optional().describe("Override shell (e.g., /bin/zsh, /bin/bash)"),
5422
5429
  agentId: external_exports.string().optional().describe("Identifier for the owning agent/subagent"),
5423
5430
  timeoutMs: external_exports.coerce.number().min(1e3).max(3e5).optional().default(3e4).describe("Timeout in milliseconds (default: 30000, max: 300000)"),
5424
- waitForCompletion: external_exports.coerce.boolean().optional().default(true).describe("Wait for command to complete before returning (default: true)")
5431
+ waitForCompletion: coerceBoolean.optional().default(true).describe("Wait for command to complete before returning (default: true)")
5425
5432
  });
5426
5433
  var terminalSendInputSchema = external_exports.object({
5427
5434
  sessionId: external_exports.string().min(1).describe("Session ID of the target terminal"),
@@ -5518,11 +5525,12 @@ async function handleTerminalExecute(params, sessionManager2) {
5518
5525
  async function handleTerminalRun(params, sessionManager2) {
5519
5526
  const input = terminalRunSchema.parse(params);
5520
5527
  let sessionId;
5528
+ let isNewSession = false;
5521
5529
  const existing = sessionManager2.listSessions(input.agentId);
5522
5530
  for (const s of existing) {
5523
5531
  if (!s.isActive || input.cwd && s.cwd !== input.cwd) continue;
5524
- const session2 = sessionManager2.getSession(s.sessionId);
5525
- if (session2 && !session2.isBusy) {
5532
+ const session = sessionManager2.getSession(s.sessionId);
5533
+ if (session && !session.isBusy) {
5526
5534
  sessionId = s.sessionId;
5527
5535
  break;
5528
5536
  }
@@ -5536,44 +5544,43 @@ async function handleTerminalRun(params, sessionManager2) {
5536
5544
  agentId: input.agentId
5537
5545
  });
5538
5546
  sessionId = sessionInfo.sessionId;
5547
+ isNewSession = true;
5548
+ }
5549
+ if (isNewSession) {
5550
+ return new Promise((resolve) => {
5551
+ setTimeout(async () => {
5552
+ const result = await executeCommand(sessionId, input, sessionManager2);
5553
+ resolve(result);
5554
+ }, 500);
5555
+ });
5539
5556
  }
5557
+ return executeCommand(sessionId, input, sessionManager2);
5558
+ }
5559
+ async function executeCommand(sessionId, input, sessionManager2) {
5540
5560
  const session = sessionManager2.getSession(sessionId);
5541
5561
  if (!session) {
5542
5562
  return {
5543
- content: [
5544
- {
5545
- type: "text",
5546
- text: "Error: Failed to get terminal session."
5547
- }
5548
- ],
5563
+ content: [{ type: "text", text: "Error: Failed to get terminal session." }],
5549
5564
  isError: true
5550
5565
  };
5551
5566
  }
5552
5567
  const validation = sessionManager2.validateCommand(input.command);
5553
5568
  if (!validation.valid) {
5554
5569
  return {
5555
- content: [
5556
- {
5557
- type: "text",
5558
- text: `Command blocked: ${validation.reason}`
5559
- }
5560
- ],
5570
+ content: [{ type: "text", text: `Command blocked: ${validation.reason}` }],
5561
5571
  isError: true
5562
5572
  };
5563
5573
  }
5564
5574
  const timeoutMs = input.timeoutMs ?? sessionManager2.getDefaultTimeout();
5565
5575
  const waitForCompletion = input.waitForCompletion ?? true;
5566
- const result = await session.execute(
5567
- input.command,
5568
- timeoutMs,
5569
- waitForCompletion
5570
- );
5571
- const cleanOutput = result.output.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "").trim();
5572
- const statusParts = [
5573
- `exit: ${result.exitCode ?? "n/a"}`,
5574
- `${result.durationMs}ms`,
5575
- sessionId
5576
- ];
5576
+ const result = await session.execute(input.command, timeoutMs, waitForCompletion);
5577
+ let cleanOutput = result.output.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "").trim();
5578
+ const lines = cleanOutput.split("\n");
5579
+ if (lines.length > 0 && lines[0].trim() === input.command.trim()) {
5580
+ lines.shift();
5581
+ cleanOutput = lines.join("\n").trim();
5582
+ }
5583
+ const statusParts = [`exit: ${result.exitCode ?? "n/a"}`, `${result.durationMs}ms`, sessionId];
5577
5584
  let text = `$ ${input.command}
5578
5585
  ${cleanOutput}
5579
5586
 
@@ -5583,12 +5590,7 @@ ${cleanOutput}
5583
5590
  [TIMED OUT after ${timeoutMs}ms - session still active, use read to get more output]`;
5584
5591
  }
5585
5592
  return {
5586
- content: [
5587
- {
5588
- type: "text",
5589
- text
5590
- }
5591
- ],
5593
+ content: [{ type: "text", text }],
5592
5594
  isError: result.exitCode !== null && result.exitCode !== 0
5593
5595
  };
5594
5596
  }
@@ -5609,23 +5611,20 @@ async function handleTerminalReadOutput(params, sessionManager2) {
5609
5611
  };
5610
5612
  }
5611
5613
  const result = session.readOutput(input.offset, input.lines);
5614
+ const cleanOutput = result.lines.join("\n").replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "").trim();
5615
+ const status = [
5616
+ `lines: ${result.readFrom}-${result.readFrom + result.readCount}/${result.totalLines}`,
5617
+ `remaining: ${result.remaining}`,
5618
+ input.sessionId
5619
+ ];
5620
+ const text = `${cleanOutput}
5621
+
5622
+ [${status.join(" | ")}]`;
5612
5623
  return {
5613
5624
  content: [
5614
5625
  {
5615
5626
  type: "text",
5616
- text: JSON.stringify(
5617
- {
5618
- sessionId: input.sessionId,
5619
- readFrom: result.readFrom,
5620
- readCount: result.readCount,
5621
- totalLines: result.totalLines,
5622
- remaining: result.remaining,
5623
- isComplete: result.isComplete,
5624
- output: result.lines.join("\n")
5625
- },
5626
- null,
5627
- 2
5628
- )
5627
+ text
5629
5628
  }
5630
5629
  ]
5631
5630
  };
@@ -5943,7 +5942,6 @@ var TerminalSession = class {
5943
5942
  isActive = true;
5944
5943
  lastCommandAt;
5945
5944
  shellReady;
5946
- resolveShellReady;
5947
5945
  constructor(config, maxOutputLines) {
5948
5946
  this.sessionId = generateSessionId();
5949
5947
  this.name = config.name;
@@ -5959,27 +5957,12 @@ var TerminalSession = class {
5959
5957
  if (config.shell) {
5960
5958
  terminalOptions.shellPath = config.shell;
5961
5959
  }
5962
- this.shellReady = new Promise((resolve) => {
5963
- this.resolveShellReady = resolve;
5964
- });
5965
5960
  this.terminal = vscode2.window.createTerminal(terminalOptions);
5966
5961
  this.terminal.show(true);
5962
+ this.shellReady = new Promise((resolve) => {
5963
+ resolve();
5964
+ });
5967
5965
  this.setupShellIntegrationCapture();
5968
- if (vscode2.window.onDidChangeTerminalShellIntegration) {
5969
- const disposable = vscode2.window.onDidChangeTerminalShellIntegration((e) => {
5970
- if (e.terminal === this.terminal) {
5971
- disposable.dispose();
5972
- log(`Shell integration ready for session ${this.sessionId}`);
5973
- this.resolveShellReady();
5974
- }
5975
- });
5976
- setTimeout(() => {
5977
- disposable.dispose();
5978
- this.resolveShellReady();
5979
- }, 3e3);
5980
- } else {
5981
- setTimeout(() => this.resolveShellReady(), 1500);
5982
- }
5983
5966
  log(`Session ${this.sessionId} created: ${config.name} (cwd: ${this.cwd})`);
5984
5967
  }
5985
5968
  setupShellIntegrationCapture() {
@@ -6011,6 +5994,7 @@ var TerminalSession = class {
6011
5994
  this.commandHistory.push(this.currentCommand);
6012
5995
  this.currentCommand = null;
6013
5996
  }
5997
+ this.lastCommandAt = Date.now();
6014
5998
  log(
6015
5999
  `Shell execution ended in session ${this.sessionId} with exit code: ${event.exitCode}`
6016
6000
  );
@@ -6018,11 +6002,10 @@ var TerminalSession = class {
6018
6002
  }
6019
6003
  }
6020
6004
  }
6021
- /**
6022
- * Execute a command in this terminal session.
6023
- */
6024
6005
  async execute(command, timeoutMs, waitForCompletion) {
6006
+ log(`Waiting for shell ready in session ${this.sessionId}...`);
6025
6007
  await this.shellReady;
6008
+ log(`Shell ready, executing command in session ${this.sessionId}`);
6026
6009
  const commandId = generateCommandId();
6027
6010
  const startedAt = Date.now();
6028
6011
  this.lastCommandAt = startedAt;
@@ -6109,9 +6092,6 @@ var TerminalSession = class {
6109
6092
  }
6110
6093
  });
6111
6094
  }
6112
- /**
6113
- * Send text input to the terminal (for interactive commands).
6114
- */
6115
6095
  sendInput(input, pressEnter) {
6116
6096
  this.terminal.sendText(input, pressEnter);
6117
6097
  this.lastCommandAt = Date.now();
@@ -6119,21 +6099,12 @@ var TerminalSession = class {
6119
6099
  `Input sent to session ${this.sessionId}: ${input.slice(0, 50)}${input.length > 50 ? "..." : ""}`
6120
6100
  );
6121
6101
  }
6122
- /**
6123
- * Read output from the buffer with pagination.
6124
- */
6125
6102
  readOutput(offset = 0, maxLines = 500) {
6126
6103
  return readFromBuffer(this.outputBuffer, offset, maxLines);
6127
6104
  }
6128
- /**
6129
- * Check if a command is currently executing.
6130
- */
6131
6105
  get isBusy() {
6132
6106
  return this.currentCommand !== null;
6133
6107
  }
6134
- /**
6135
- * Get session info for listing.
6136
- */
6137
6108
  getInfo() {
6138
6109
  return {
6139
6110
  sessionId: this.sessionId,
@@ -6146,23 +6117,14 @@ var TerminalSession = class {
6146
6117
  outputLineCount: getBufferLineCount(this.outputBuffer)
6147
6118
  };
6148
6119
  }
6149
- /**
6150
- * Get the VSCode terminal instance (for matching events).
6151
- */
6152
6120
  getTerminal() {
6153
6121
  return this.terminal;
6154
6122
  }
6155
- /**
6156
- * Check if session has been idle longer than the given duration.
6157
- */
6158
6123
  isIdle(idleThresholdMs) {
6159
6124
  if (idleThresholdMs <= 0) return false;
6160
6125
  const lastActivity = this.lastCommandAt ?? this.createdAt;
6161
6126
  return Date.now() - lastActivity > idleThresholdMs;
6162
6127
  }
6163
- /**
6164
- * Close the terminal session.
6165
- */
6166
6128
  dispose() {
6167
6129
  this.isActive = false;
6168
6130
  this.shellExecutionDisposable?.dispose();
@@ -6178,7 +6140,6 @@ var SessionManager = class {
6178
6140
  onSessionsChanged = this.onSessionsChangedEmitter.event;
6179
6141
  idleReaperInterval = null;
6180
6142
  constructor() {
6181
- this.startIdleReaper();
6182
6143
  vscode3.window.onDidCloseTerminal((terminal) => {
6183
6144
  for (const [id, session] of this.sessions) {
6184
6145
  if (session.getTerminal() === terminal) {
@@ -6212,6 +6173,7 @@ var SessionManager = class {
6212
6173
  const config = this.getConfig();
6213
6174
  if (config.idleTimeoutMs <= 0) return;
6214
6175
  for (const [id, session] of this.sessions) {
6176
+ if (session.isBusy) continue;
6215
6177
  if (session.isIdle(config.idleTimeoutMs)) {
6216
6178
  log(`Reaping idle session ${id}`);
6217
6179
  session.dispose();
package/dist/mcp-entry.js CHANGED
File without changes
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "vscode-terminal-mcp",
3
3
  "displayName": "Terminal MCP Server",
4
4
  "description": "MCP server that provides visible terminal sessions in VSCode for Claude Code and subagents",
5
- "version": "0.1.3",
5
+ "version": "0.1.5",
6
6
  "publisher": "sirlordt",
7
7
  "license": "MIT",
8
8
  "author": "sirlordt",