vscode-terminal-mcp 0.1.4 → 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 +46 -89
- 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,49 +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
|
-
);
|
|
5576
|
+
const result = await session.execute(input.command, timeoutMs, waitForCompletion);
|
|
5571
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();
|
|
5572
5578
|
const lines = cleanOutput.split("\n");
|
|
5573
5579
|
if (lines.length > 0 && lines[0].trim() === input.command.trim()) {
|
|
5574
5580
|
lines.shift();
|
|
5575
5581
|
cleanOutput = lines.join("\n").trim();
|
|
5576
5582
|
}
|
|
5577
|
-
const statusParts = [
|
|
5578
|
-
`exit: ${result.exitCode ?? "n/a"}`,
|
|
5579
|
-
`${result.durationMs}ms`,
|
|
5580
|
-
sessionId
|
|
5581
|
-
];
|
|
5583
|
+
const statusParts = [`exit: ${result.exitCode ?? "n/a"}`, `${result.durationMs}ms`, sessionId];
|
|
5582
5584
|
let text = `$ ${input.command}
|
|
5583
5585
|
${cleanOutput}
|
|
5584
5586
|
|
|
@@ -5588,12 +5590,7 @@ ${cleanOutput}
|
|
|
5588
5590
|
[TIMED OUT after ${timeoutMs}ms - session still active, use read to get more output]`;
|
|
5589
5591
|
}
|
|
5590
5592
|
return {
|
|
5591
|
-
content: [
|
|
5592
|
-
{
|
|
5593
|
-
type: "text",
|
|
5594
|
-
text
|
|
5595
|
-
}
|
|
5596
|
-
],
|
|
5593
|
+
content: [{ type: "text", text }],
|
|
5597
5594
|
isError: result.exitCode !== null && result.exitCode !== 0
|
|
5598
5595
|
};
|
|
5599
5596
|
}
|
|
@@ -5614,23 +5611,20 @@ async function handleTerminalReadOutput(params, sessionManager2) {
|
|
|
5614
5611
|
};
|
|
5615
5612
|
}
|
|
5616
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(" | ")}]`;
|
|
5617
5623
|
return {
|
|
5618
5624
|
content: [
|
|
5619
5625
|
{
|
|
5620
5626
|
type: "text",
|
|
5621
|
-
text
|
|
5622
|
-
{
|
|
5623
|
-
sessionId: input.sessionId,
|
|
5624
|
-
readFrom: result.readFrom,
|
|
5625
|
-
readCount: result.readCount,
|
|
5626
|
-
totalLines: result.totalLines,
|
|
5627
|
-
remaining: result.remaining,
|
|
5628
|
-
isComplete: result.isComplete,
|
|
5629
|
-
output: result.lines.join("\n")
|
|
5630
|
-
},
|
|
5631
|
-
null,
|
|
5632
|
-
2
|
|
5633
|
-
)
|
|
5627
|
+
text
|
|
5634
5628
|
}
|
|
5635
5629
|
]
|
|
5636
5630
|
};
|
|
@@ -5948,7 +5942,6 @@ var TerminalSession = class {
|
|
|
5948
5942
|
isActive = true;
|
|
5949
5943
|
lastCommandAt;
|
|
5950
5944
|
shellReady;
|
|
5951
|
-
resolveShellReady;
|
|
5952
5945
|
constructor(config, maxOutputLines) {
|
|
5953
5946
|
this.sessionId = generateSessionId();
|
|
5954
5947
|
this.name = config.name;
|
|
@@ -5964,27 +5957,12 @@ var TerminalSession = class {
|
|
|
5964
5957
|
if (config.shell) {
|
|
5965
5958
|
terminalOptions.shellPath = config.shell;
|
|
5966
5959
|
}
|
|
5967
|
-
this.shellReady = new Promise((resolve) => {
|
|
5968
|
-
this.resolveShellReady = resolve;
|
|
5969
|
-
});
|
|
5970
5960
|
this.terminal = vscode2.window.createTerminal(terminalOptions);
|
|
5971
5961
|
this.terminal.show(true);
|
|
5962
|
+
this.shellReady = new Promise((resolve) => {
|
|
5963
|
+
resolve();
|
|
5964
|
+
});
|
|
5972
5965
|
this.setupShellIntegrationCapture();
|
|
5973
|
-
if (vscode2.window.onDidChangeTerminalShellIntegration) {
|
|
5974
|
-
const disposable = vscode2.window.onDidChangeTerminalShellIntegration((e) => {
|
|
5975
|
-
if (e.terminal === this.terminal) {
|
|
5976
|
-
disposable.dispose();
|
|
5977
|
-
log(`Shell integration ready for session ${this.sessionId}`);
|
|
5978
|
-
this.resolveShellReady();
|
|
5979
|
-
}
|
|
5980
|
-
});
|
|
5981
|
-
setTimeout(() => {
|
|
5982
|
-
disposable.dispose();
|
|
5983
|
-
this.resolveShellReady();
|
|
5984
|
-
}, 3e3);
|
|
5985
|
-
} else {
|
|
5986
|
-
setTimeout(() => this.resolveShellReady(), 1500);
|
|
5987
|
-
}
|
|
5988
5966
|
log(`Session ${this.sessionId} created: ${config.name} (cwd: ${this.cwd})`);
|
|
5989
5967
|
}
|
|
5990
5968
|
setupShellIntegrationCapture() {
|
|
@@ -6016,6 +5994,7 @@ var TerminalSession = class {
|
|
|
6016
5994
|
this.commandHistory.push(this.currentCommand);
|
|
6017
5995
|
this.currentCommand = null;
|
|
6018
5996
|
}
|
|
5997
|
+
this.lastCommandAt = Date.now();
|
|
6019
5998
|
log(
|
|
6020
5999
|
`Shell execution ended in session ${this.sessionId} with exit code: ${event.exitCode}`
|
|
6021
6000
|
);
|
|
@@ -6023,11 +6002,10 @@ var TerminalSession = class {
|
|
|
6023
6002
|
}
|
|
6024
6003
|
}
|
|
6025
6004
|
}
|
|
6026
|
-
/**
|
|
6027
|
-
* Execute a command in this terminal session.
|
|
6028
|
-
*/
|
|
6029
6005
|
async execute(command, timeoutMs, waitForCompletion) {
|
|
6006
|
+
log(`Waiting for shell ready in session ${this.sessionId}...`);
|
|
6030
6007
|
await this.shellReady;
|
|
6008
|
+
log(`Shell ready, executing command in session ${this.sessionId}`);
|
|
6031
6009
|
const commandId = generateCommandId();
|
|
6032
6010
|
const startedAt = Date.now();
|
|
6033
6011
|
this.lastCommandAt = startedAt;
|
|
@@ -6114,9 +6092,6 @@ var TerminalSession = class {
|
|
|
6114
6092
|
}
|
|
6115
6093
|
});
|
|
6116
6094
|
}
|
|
6117
|
-
/**
|
|
6118
|
-
* Send text input to the terminal (for interactive commands).
|
|
6119
|
-
*/
|
|
6120
6095
|
sendInput(input, pressEnter) {
|
|
6121
6096
|
this.terminal.sendText(input, pressEnter);
|
|
6122
6097
|
this.lastCommandAt = Date.now();
|
|
@@ -6124,21 +6099,12 @@ var TerminalSession = class {
|
|
|
6124
6099
|
`Input sent to session ${this.sessionId}: ${input.slice(0, 50)}${input.length > 50 ? "..." : ""}`
|
|
6125
6100
|
);
|
|
6126
6101
|
}
|
|
6127
|
-
/**
|
|
6128
|
-
* Read output from the buffer with pagination.
|
|
6129
|
-
*/
|
|
6130
6102
|
readOutput(offset = 0, maxLines = 500) {
|
|
6131
6103
|
return readFromBuffer(this.outputBuffer, offset, maxLines);
|
|
6132
6104
|
}
|
|
6133
|
-
/**
|
|
6134
|
-
* Check if a command is currently executing.
|
|
6135
|
-
*/
|
|
6136
6105
|
get isBusy() {
|
|
6137
6106
|
return this.currentCommand !== null;
|
|
6138
6107
|
}
|
|
6139
|
-
/**
|
|
6140
|
-
* Get session info for listing.
|
|
6141
|
-
*/
|
|
6142
6108
|
getInfo() {
|
|
6143
6109
|
return {
|
|
6144
6110
|
sessionId: this.sessionId,
|
|
@@ -6151,23 +6117,14 @@ var TerminalSession = class {
|
|
|
6151
6117
|
outputLineCount: getBufferLineCount(this.outputBuffer)
|
|
6152
6118
|
};
|
|
6153
6119
|
}
|
|
6154
|
-
/**
|
|
6155
|
-
* Get the VSCode terminal instance (for matching events).
|
|
6156
|
-
*/
|
|
6157
6120
|
getTerminal() {
|
|
6158
6121
|
return this.terminal;
|
|
6159
6122
|
}
|
|
6160
|
-
/**
|
|
6161
|
-
* Check if session has been idle longer than the given duration.
|
|
6162
|
-
*/
|
|
6163
6123
|
isIdle(idleThresholdMs) {
|
|
6164
6124
|
if (idleThresholdMs <= 0) return false;
|
|
6165
6125
|
const lastActivity = this.lastCommandAt ?? this.createdAt;
|
|
6166
6126
|
return Date.now() - lastActivity > idleThresholdMs;
|
|
6167
6127
|
}
|
|
6168
|
-
/**
|
|
6169
|
-
* Close the terminal session.
|
|
6170
|
-
*/
|
|
6171
6128
|
dispose() {
|
|
6172
6129
|
this.isActive = false;
|
|
6173
6130
|
this.shellExecutionDisposable?.dispose();
|
|
@@ -6183,7 +6140,6 @@ var SessionManager = class {
|
|
|
6183
6140
|
onSessionsChanged = this.onSessionsChangedEmitter.event;
|
|
6184
6141
|
idleReaperInterval = null;
|
|
6185
6142
|
constructor() {
|
|
6186
|
-
this.startIdleReaper();
|
|
6187
6143
|
vscode3.window.onDidCloseTerminal((terminal) => {
|
|
6188
6144
|
for (const [id, session] of this.sessions) {
|
|
6189
6145
|
if (session.getTerminal() === terminal) {
|
|
@@ -6217,6 +6173,7 @@ var SessionManager = class {
|
|
|
6217
6173
|
const config = this.getConfig();
|
|
6218
6174
|
if (config.idleTimeoutMs <= 0) return;
|
|
6219
6175
|
for (const [id, session] of this.sessions) {
|
|
6176
|
+
if (session.isBusy) continue;
|
|
6220
6177
|
if (session.isIdle(config.idleTimeoutMs)) {
|
|
6221
6178
|
log(`Reaping idle session ${id}`);
|
|
6222
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",
|