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 +52 -90
- package/dist/mcp-entry.js +0 -0
- package/package.json +1 -1
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:
|
|
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:
|
|
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
|
|
5525
|
-
if (
|
|
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
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
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
|
|
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.
|
|
5
|
+
"version": "0.1.5",
|
|
6
6
|
"publisher": "sirlordt",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"author": "sirlordt",
|