@hcu-lab.me/mcp-device 1.0.6 → 1.0.8
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 +7 -71
- package/dist/bootstrap.js +1 -1
- package/dist/config-manager.js +1 -1
- package/dist/config.js +1 -1
- package/dist/custom-stdio.js +1 -2
- package/dist/data/onboarding-prompts.json +22 -11
- package/dist/device/device-state.d.ts +0 -5
- package/dist/device/device-state.js +0 -84
- package/dist/device/device.d.ts +1 -1
- package/dist/device/device.js +8 -7
- package/dist/device/execution-engine.d.ts +10 -1
- package/dist/device/execution-engine.js +81 -128
- package/dist/device/gateway-channel.d.ts +6 -0
- package/dist/device/gateway-channel.js +16 -1
- package/dist/device/gateway-tool-adapter.d.ts +3 -3
- package/dist/device/gateway-tool-adapter.js +12 -11
- package/dist/device/linux-service.d.ts +4 -0
- package/dist/device/linux-service.js +10 -3
- package/dist/device/runtime-owner.js +2 -2
- package/dist/device/self-update.js +3 -1
- package/dist/device/windows-service.d.ts +3 -4
- package/dist/device/windows-service.js +5 -44
- package/dist/npm-scripts/remote.js +0 -2
- package/dist/server.js +9 -99
- package/dist/terminal-manager.js +1 -1
- package/dist/tools/config.d.ts +1 -1
- package/dist/tools/filesystem.js +3 -3
- package/dist/tools/pdf/markdown.js +1 -1
- package/dist/tools/schemas.d.ts +2 -3
- package/dist/tools/schemas.js +0 -10
- package/dist/ui/config-editor/index.html +1 -1
- package/dist/ui/file-preview/index.html +1 -1
- package/dist/ui/file-preview/styles.css +2 -2
- package/dist/utils/capture.d.ts +5 -28
- package/dist/utils/capture.js +11 -455
- package/dist/utils/feature-flags.d.ts +0 -47
- package/dist/utils/feature-flags.js +5 -199
- package/dist/utils/files/docx.d.ts +2 -2
- package/dist/utils/files/docx.js +2 -2
- package/dist/utils/usageTracker.js +0 -24
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -6
- package/dist/tools/feedback.d.ts +0 -8
- package/dist/tools/feedback.js +0 -132
|
@@ -21,12 +21,11 @@ function isMissingRegistrationError(error) {
|
|
|
21
21
|
export function buildWindowsTaskName(deviceId) {
|
|
22
22
|
return `MCP-Device-${taskSafeDeviceId(deviceId)}`;
|
|
23
23
|
}
|
|
24
|
-
function legacyWindowsTaskName(deviceId) {
|
|
25
|
-
return `HCU-Device-${taskSafeDeviceId(deviceId)}`;
|
|
26
|
-
}
|
|
27
24
|
export function buildWindowsRunnerScript(options) {
|
|
25
|
+
const runtimeDir = options.runtimeDir ? String(options.runtimeDir) : deviceStatePaths().runtime;
|
|
28
26
|
return [
|
|
29
27
|
"$ErrorActionPreference = 'Stop'",
|
|
28
|
+
`Set-Location -LiteralPath ${psQuote(runtimeDir)}`,
|
|
30
29
|
`& ${psQuote(options.nodePath)} ${psQuote(options.entrypoint)} --service`,
|
|
31
30
|
'exit $LASTEXITCODE',
|
|
32
31
|
''
|
|
@@ -40,9 +39,9 @@ export class WindowsDeviceService {
|
|
|
40
39
|
return { stdout: String(result.stdout || ''), stderr: String(result.stderr || '') };
|
|
41
40
|
});
|
|
42
41
|
this.runnerPath = path.resolve(options.runnerPath || path.join(deviceStatePaths().root, 'run-device.ps1'));
|
|
43
|
-
this.legacyRunnerPath = path.resolve(options.legacyRunnerPath || path.join(deviceStatePaths().legacyRoot, 'run-device.ps1'));
|
|
44
42
|
this.nodePath = path.resolve(options.nodePath || process.execPath);
|
|
45
43
|
this.entrypoint = path.resolve(options.entrypoint || process.argv[1]);
|
|
44
|
+
this.runtimeDir = options.runtimeDir ? String(options.runtimeDir) : deviceStatePaths().runtime;
|
|
46
45
|
}
|
|
47
46
|
assertWindows() {
|
|
48
47
|
if (this.platform !== 'win32')
|
|
@@ -91,41 +90,6 @@ export class WindowsDeviceService {
|
|
|
91
90
|
}
|
|
92
91
|
catch { /* absent is fine */ }
|
|
93
92
|
}
|
|
94
|
-
commandReferencesLegacyRunner(value) {
|
|
95
|
-
const normalize = (text) => String(text || '').replace(/\//g, '\\').toLowerCase();
|
|
96
|
-
return normalize(value).includes(normalize(this.legacyRunnerPath));
|
|
97
|
-
}
|
|
98
|
-
async deleteVerifiedLegacyRegistrations(taskName) {
|
|
99
|
-
let scheduledAction = null;
|
|
100
|
-
try {
|
|
101
|
-
const command = `(Get-ScheduledTask -TaskName ${psQuote(taskName)} -ErrorAction Stop).Actions | ForEach-Object { ($_.Execute + ' ' + $_.Arguments) }`;
|
|
102
|
-
scheduledAction = String((await this.execFile('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', command])).stdout || '');
|
|
103
|
-
}
|
|
104
|
-
catch { /* absent scheduled task is fine */ }
|
|
105
|
-
if (scheduledAction !== null && this.commandReferencesLegacyRunner(scheduledAction)) {
|
|
106
|
-
try {
|
|
107
|
-
await this.execFile('schtasks.exe', ['/Delete', '/F', '/TN', taskName]);
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
if (!isMissingRegistrationError(error))
|
|
111
|
-
throw error;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
let runValue = null;
|
|
115
|
-
try {
|
|
116
|
-
runValue = String((await this.execFile('reg.exe', ['query', USER_RUN_KEY, '/v', taskName])).stdout || '');
|
|
117
|
-
}
|
|
118
|
-
catch { /* absent Run value is fine */ }
|
|
119
|
-
if (runValue !== null && this.commandReferencesLegacyRunner(runValue)) {
|
|
120
|
-
try {
|
|
121
|
-
await this.execFile('reg.exe', ['delete', USER_RUN_KEY, '/v', taskName, '/f']);
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
if (!isMissingRegistrationError(error))
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
93
|
async createAutostartRegistration(taskName) {
|
|
130
94
|
const taskAction = `powershell.exe -NoProfile -ExecutionPolicy Bypass -File "${this.runnerPath}"`;
|
|
131
95
|
try {
|
|
@@ -157,13 +121,12 @@ export class WindowsDeviceService {
|
|
|
157
121
|
async install(options) {
|
|
158
122
|
this.assertWindows();
|
|
159
123
|
const taskName = buildWindowsTaskName(options.deviceId);
|
|
160
|
-
const legacyTaskName = legacyWindowsTaskName(options.deviceId);
|
|
161
124
|
const existing = await this.status(options.deviceId).catch(() => null);
|
|
162
125
|
const preserveManual = existing?.installed === true && existing.autostart === false;
|
|
163
|
-
const runner = buildWindowsRunnerScript({ nodePath: this.nodePath, entrypoint: this.entrypoint });
|
|
126
|
+
const runner = buildWindowsRunnerScript({ nodePath: this.nodePath, entrypoint: this.entrypoint, runtimeDir: this.runtimeDir });
|
|
127
|
+
await fs.mkdir(this.runtimeDir, { recursive: true, mode: 0o700 });
|
|
164
128
|
await fs.mkdir(path.dirname(this.runnerPath), { recursive: true });
|
|
165
129
|
await fs.writeFile(this.runnerPath, runner, { mode: 0o600 });
|
|
166
|
-
await this.deleteVerifiedLegacyRegistrations(legacyTaskName);
|
|
167
130
|
if (preserveManual) {
|
|
168
131
|
await this.deleteRegistrations(taskName);
|
|
169
132
|
return;
|
|
@@ -226,13 +189,11 @@ export class WindowsDeviceService {
|
|
|
226
189
|
async uninstall(deviceId) {
|
|
227
190
|
this.assertWindows();
|
|
228
191
|
const taskName = buildWindowsTaskName(deviceId);
|
|
229
|
-
const legacyTaskName = legacyWindowsTaskName(deviceId);
|
|
230
192
|
try {
|
|
231
193
|
await this.stop(deviceId);
|
|
232
194
|
}
|
|
233
195
|
catch { /* registration cleanup remains authoritative; runtime is never PID-killed. */ }
|
|
234
196
|
await this.deleteRegistrations(taskName);
|
|
235
|
-
await this.deleteVerifiedLegacyRegistrations(legacyTaskName);
|
|
236
197
|
await fs.rm(this.runnerPath, { force: true });
|
|
237
198
|
}
|
|
238
199
|
}
|
|
@@ -325,8 +325,6 @@ async function serviceCommand(command) {
|
|
|
325
325
|
}
|
|
326
326
|
export async function runRemote() {
|
|
327
327
|
// MCP Device owns its account-scoped usage telemetry at the gateway.
|
|
328
|
-
// Never send inherited Desktop Commander analytics from the remote execution path.
|
|
329
|
-
process.env.DESKTOP_COMMANDER_DISABLE_TELEMETRY = 'true';
|
|
330
328
|
const disableNoSleep = process.argv.includes('--disable-no-sleep');
|
|
331
329
|
const verbose = process.argv.includes('--debug');
|
|
332
330
|
if (!verbose)
|
package/dist/server.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
2
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, ListPromptsRequestSchema, InitializeRequestSchema, LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
3
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
@@ -9,11 +8,10 @@ const OS_GUIDANCE = getOSSpecificGuidance(SYSTEM_INFO);
|
|
|
9
8
|
const DEV_TOOL_GUIDANCE = getDevelopmentToolGuidance(SYSTEM_INFO);
|
|
10
9
|
const PATH_GUIDANCE = `IMPORTANT: ${getPathGuidance(SYSTEM_INFO)} Relative paths may fail as they depend on the current working directory. Tilde paths (~/...) might not work in all contexts. Unless the user explicitly asks for relative paths, use absolute paths.`;
|
|
11
10
|
const CMD_PREFIX_DESCRIPTION = `This command can be referenced as "MCP Device: ..." or "use MCP Device to ..." in your instructions.`;
|
|
12
|
-
import { StartProcessArgsSchema, ReadProcessOutputArgsSchema, InteractWithProcessArgsSchema, ForceTerminateArgsSchema, ListSessionsArgsSchema, KillProcessArgsSchema, ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, GetFileInfoArgsSchema, GetConfigArgsSchema, SetConfigValueArgsSchema, ListProcessesArgsSchema, EditBlockArgsSchema, GetUsageStatsArgsSchema,
|
|
11
|
+
import { StartProcessArgsSchema, ReadProcessOutputArgsSchema, InteractWithProcessArgsSchema, ForceTerminateArgsSchema, ListSessionsArgsSchema, KillProcessArgsSchema, ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, GetFileInfoArgsSchema, GetConfigArgsSchema, SetConfigValueArgsSchema, ListProcessesArgsSchema, EditBlockArgsSchema, GetUsageStatsArgsSchema, StartSearchArgsSchema, GetMoreSearchResultsArgsSchema, StopSearchArgsSchema, ListSearchesArgsSchema, GetPromptsArgsSchema, GetRecentToolCallsArgsSchema, WritePdfArgsSchema, toolArgSchemas, } from './tools/schemas.js';
|
|
13
12
|
import { detectUnsupportedParams, getSupportedParams, buildUnsupportedParamsWarning, } from './utils/unsupportedParams.js';
|
|
14
13
|
import { getConfig, setConfigValue } from './tools/config.js';
|
|
15
14
|
import { getUsageStats } from './tools/usage.js';
|
|
16
|
-
import { giveFeedbackToDesktopCommander } from './tools/feedback.js';
|
|
17
15
|
import { getPrompts } from './tools/prompts.js';
|
|
18
16
|
import { trackToolCall } from './utils/trackTools.js';
|
|
19
17
|
import { usageTracker } from './utils/usageTracker.js';
|
|
@@ -89,7 +87,7 @@ function setCurrentCallIsRemote(isRemote) {
|
|
|
89
87
|
currentCallIsRemote = isRemote;
|
|
90
88
|
}
|
|
91
89
|
// The remote caller's client for the in-flight tool call (e.g. openai-mcp,
|
|
92
|
-
//
|
|
90
|
+
// remote client). Set per CallTool when the call is remote; null for local calls.
|
|
93
91
|
// Mirrors currentCallIsRemote so telemetry attributes remote events to the
|
|
94
92
|
// actual remote client instead of the device's own currentClient (which stays
|
|
95
93
|
// LOCAL and must not be polluted by remote callers).
|
|
@@ -104,11 +102,10 @@ function setCurrentRemoteClient(clientInfo) {
|
|
|
104
102
|
/**
|
|
105
103
|
* True when this server instance is serving remote services rather than a
|
|
106
104
|
* local MCP client. The device wrapper marks the server it spawns with
|
|
107
|
-
* MCP_DEVICE_REMOTE=true (see device/execution-engine.ts)
|
|
108
|
-
* the client-name check covers older wrappers that predate the env marker.
|
|
105
|
+
* MCP_DEVICE_REMOTE=true (see device/execution-engine.ts).
|
|
109
106
|
*/
|
|
110
107
|
function isRemoteClientContext(clientName) {
|
|
111
|
-
return process.env.MCP_DEVICE_REMOTE === 'true'
|
|
108
|
+
return process.env.MCP_DEVICE_REMOTE === 'true';
|
|
112
109
|
}
|
|
113
110
|
/**
|
|
114
111
|
* Unified way to update client information
|
|
@@ -138,44 +135,19 @@ server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
|
138
135
|
const clientInfo = request.params?.clientInfo;
|
|
139
136
|
if (clientInfo) {
|
|
140
137
|
await updateCurrentClient(clientInfo);
|
|
141
|
-
|
|
142
|
-
// the Desktop Commander app and remote contexts. Further exclusions are
|
|
143
|
-
// flag-served via welcome_page_excluded_clients (e.g. claude-code, which
|
|
144
|
-
// covers Claude Code and Cowork plugin sessions — both identify as
|
|
145
|
-
// `claude-code` and provide their own onboarding surface).
|
|
146
|
-
const isWelcomePageEligibleClient = currentClient.name !== 'desktop-commander-app'
|
|
147
|
-
&& currentClient.name !== 'desktop-commander'
|
|
148
|
-
&& !isRemoteClientContext(currentClient.name)
|
|
138
|
+
const isWelcomePageEligibleClient = !isRemoteClientContext(currentClient.name)
|
|
149
139
|
&& !global.disableOnboarding;
|
|
150
140
|
if (isRemoteClientContext(currentClient.name)) {
|
|
151
|
-
// Remote
|
|
152
|
-
// state merely because they initialized an inherited MCP server.
|
|
141
|
+
// Remote child engines do not mutate local onboarding state.
|
|
153
142
|
}
|
|
154
143
|
else if (isWelcomePageEligibleClient) {
|
|
155
144
|
await handleWelcomePageOnboarding(currentClient.name);
|
|
156
145
|
}
|
|
157
146
|
else {
|
|
158
|
-
// Do not carry a first-run page over to a client that is made
|
|
159
|
-
// eligible in a later release.
|
|
160
147
|
await skipWelcomePageOnboarding();
|
|
161
148
|
}
|
|
149
|
+
capture('run_server_mcp_initialized');
|
|
162
150
|
}
|
|
163
|
-
// Raw host environment signals (no PII, undefined when absent). Some
|
|
164
|
-
// hosts share a clientInfo name — Claude Code CLI, Claude Code inside
|
|
165
|
-
// the Claude Desktop app, and Cowork all report 'claude-code' — and
|
|
166
|
-
// these let analytics tell them apart without client-specific
|
|
167
|
-
// branching in code. Verified signatures: CLI → entrypoint 'cli';
|
|
168
|
-
// CC-in-desktop → entrypoint 'claude-desktop'; Cowork → no
|
|
169
|
-
// entrypoint/agent, plugin id 'desktop-commander-inline'.
|
|
170
|
-
// Values truncated to GA4's 100-char param limit (same convention as
|
|
171
|
-
// containerName/containerImage) so an oversized value can never get
|
|
172
|
-
// the whole event rejected.
|
|
173
|
-
capture('run_server_mcp_initialized', {
|
|
174
|
-
host_entrypoint: process.env.CLAUDE_CODE_ENTRYPOINT?.substring(0, 100),
|
|
175
|
-
host_agent: process.env.AI_AGENT?.substring(0, 100),
|
|
176
|
-
host_plugin_id: process.env.CLAUDE_PLUGIN_DATA
|
|
177
|
-
? path.basename(process.env.CLAUDE_PLUGIN_DATA).substring(0, 100) : undefined
|
|
178
|
-
});
|
|
179
151
|
// Negotiate protocol version with client
|
|
180
152
|
const requestedVersion = request.params?.protocolVersion;
|
|
181
153
|
const protocolVersion = (requestedVersion && SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion))
|
|
@@ -207,15 +179,7 @@ deferLog('info', 'Setting up request handlers...');
|
|
|
207
179
|
/**
|
|
208
180
|
* Check if a tool should be included based on current client
|
|
209
181
|
*/
|
|
210
|
-
function shouldIncludeTool(
|
|
211
|
-
// Exclude these tools for desktop-commander client (DC-specific meta-tools not useful when DC itself is the client)
|
|
212
|
-
if (currentClient?.name === 'desktop-commander-app') {
|
|
213
|
-
if (toolName === 'give_feedback_to_desktop_commander' || toolName === 'get_prompts') {
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
// Add more conditional tool logic here as needed
|
|
218
|
-
// Example: if (toolName === 'some_tool' && currentClient?.name === 'some_client') return false;
|
|
182
|
+
function shouldIncludeTool(_toolName) {
|
|
219
183
|
return true;
|
|
220
184
|
}
|
|
221
185
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -1073,48 +1037,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1073
1037
|
readOnlyHint: true,
|
|
1074
1038
|
},
|
|
1075
1039
|
},
|
|
1076
|
-
{
|
|
1077
|
-
name: "give_feedback_to_desktop_commander",
|
|
1078
|
-
description: `
|
|
1079
|
-
Open feedback form in browser to provide feedback about MCP Device.
|
|
1080
|
-
|
|
1081
|
-
IMPORTANT: This tool simply opens the feedback form - no pre-filling available.
|
|
1082
|
-
The user will fill out the form manually in their browser.
|
|
1083
|
-
|
|
1084
|
-
WORKFLOW:
|
|
1085
|
-
1. When user agrees to give feedback, just call this tool immediately
|
|
1086
|
-
2. No need to ask questions or collect information
|
|
1087
|
-
3. Tool opens form with only usage statistics pre-filled automatically:
|
|
1088
|
-
- tool_call_count: Number of commands they've made
|
|
1089
|
-
- days_using: How many days they've used MCP Device
|
|
1090
|
-
- platform: Their operating system (Mac/Windows/Linux)
|
|
1091
|
-
- client_id: Analytics identifier
|
|
1092
|
-
|
|
1093
|
-
All survey questions will be answered directly in the form:
|
|
1094
|
-
- Job title and technical comfort level
|
|
1095
|
-
- Company URL for industry context
|
|
1096
|
-
- Other AI tools they use
|
|
1097
|
-
- MCP Device's biggest advantage
|
|
1098
|
-
- How they typically use it
|
|
1099
|
-
- Recommendation likelihood (0-10)
|
|
1100
|
-
- User study participation interest
|
|
1101
|
-
- Email and any additional feedback
|
|
1102
|
-
|
|
1103
|
-
EXAMPLE INTERACTION:
|
|
1104
|
-
User: "sure, I'll give feedback"
|
|
1105
|
-
Claude: "Perfect! Let me open the feedback form for you."
|
|
1106
|
-
[calls tool immediately]
|
|
1107
|
-
|
|
1108
|
-
No parameters are needed - just call the tool to open the form.
|
|
1109
|
-
|
|
1110
|
-
${CMD_PREFIX_DESCRIPTION}`,
|
|
1111
|
-
inputSchema: zodToJsonSchema(GiveFeedbackArgsSchema),
|
|
1112
|
-
annotations: {
|
|
1113
|
-
title: "Give Feedback",
|
|
1114
|
-
readOnlyHint: false,
|
|
1115
|
-
openWorldHint: true,
|
|
1116
|
-
},
|
|
1117
|
-
},
|
|
1118
1040
|
{
|
|
1119
1041
|
name: "get_prompts",
|
|
1120
1042
|
description: `
|
|
@@ -1194,7 +1116,7 @@ async function handleCallToolRequest(request) {
|
|
|
1194
1116
|
// add remote flag (convert to string for telemetry)
|
|
1195
1117
|
telemetryData.remote = String(metadata.remote);
|
|
1196
1118
|
// Remote calls carry the originating MCP client (e.g. openai-mcp,
|
|
1197
|
-
//
|
|
1119
|
+
// remote client) in _meta.clientInfo. Attribute this call to that remote
|
|
1198
1120
|
// client — NOT the device's own currentClient. Fall back to a sentinel
|
|
1199
1121
|
// when it's absent so the call is visibly remote-but-unattributed
|
|
1200
1122
|
// rather than masquerading as the local device client. We deliberately
|
|
@@ -1336,18 +1258,6 @@ async function handleCallToolRequest(request) {
|
|
|
1336
1258
|
};
|
|
1337
1259
|
}
|
|
1338
1260
|
break;
|
|
1339
|
-
case "give_feedback_to_desktop_commander":
|
|
1340
|
-
try {
|
|
1341
|
-
result = await giveFeedbackToDesktopCommander(args);
|
|
1342
|
-
}
|
|
1343
|
-
catch (error) {
|
|
1344
|
-
capture('server_request_error', { message: `Error in give_feedback_to_desktop_commander handler: ${error}` });
|
|
1345
|
-
result = {
|
|
1346
|
-
content: [{ type: "text", text: `Error: Failed to open feedback form` }],
|
|
1347
|
-
isError: true,
|
|
1348
|
-
};
|
|
1349
|
-
}
|
|
1350
|
-
break;
|
|
1351
1261
|
// Terminal tools
|
|
1352
1262
|
case "start_process":
|
|
1353
1263
|
result = await handlers.handleStartProcess(args);
|
package/dist/terminal-manager.js
CHANGED
|
@@ -8,7 +8,7 @@ import { analyzeProcessState } from './utils/process-detection.js';
|
|
|
8
8
|
* Standard Windows PATHEXT value, used to repair a corrupted PATHEXT before
|
|
9
9
|
* spawning child shells.
|
|
10
10
|
*
|
|
11
|
-
* On some Windows
|
|
11
|
+
* On some Windows host launches the server process inherits a
|
|
12
12
|
* broken PATHEXT (observed as ".CPL" only). Because we build the child env from
|
|
13
13
|
* { ...process.env }, that broken value would propagate into every spawned
|
|
14
14
|
* shell, stripping ".EXE" and breaking resolution of git / node / python / rg /
|
package/dist/tools/config.d.ts
CHANGED
|
@@ -65,7 +65,7 @@ export declare function getConfig(): Promise<{
|
|
|
65
65
|
availableShells: string[];
|
|
66
66
|
};
|
|
67
67
|
entries: {
|
|
68
|
-
key: "
|
|
68
|
+
key: "blockedCommands" | "defaultShell" | "allowedDirectories" | "telemetryEnabled" | "fileWriteLineLimit" | "fileReadLineLimit";
|
|
69
69
|
value: unknown;
|
|
70
70
|
valueType: "string" | "number" | "boolean" | "array";
|
|
71
71
|
editable: boolean;
|
package/dist/tools/filesystem.js
CHANGED
|
@@ -84,10 +84,10 @@ function buildPermissionError(filePath, errCode) {
|
|
|
84
84
|
` 5. The app does not have permission to access this location (macOS Full Disk Access).`,
|
|
85
85
|
];
|
|
86
86
|
if (isMac) {
|
|
87
|
-
lines.push(` → Go to System Settings → Privacy & Security → Full Disk Access and enable
|
|
87
|
+
lines.push(` → Go to System Settings → Privacy & Security → Full Disk Access and enable the terminal or runtime hosting MCP Device.`);
|
|
88
88
|
lines.push(` → To open that pane directly, run in terminal:`);
|
|
89
89
|
lines.push(` open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"`);
|
|
90
|
-
lines.push(` Then
|
|
90
|
+
lines.push(` Then enable the toggle for the application that launches MCP Device.`);
|
|
91
91
|
}
|
|
92
92
|
else {
|
|
93
93
|
lines.push(` → Check that the app has permission to access this file location.`);
|
|
@@ -265,7 +265,7 @@ export async function validatePath(requestedPath) {
|
|
|
265
265
|
const pathForNextCheck = resolvedRealPath ?? absoluteOriginal;
|
|
266
266
|
// Direct Gateway device workers are already authenticated and policy-gated by
|
|
267
267
|
// the central gateway. In that dedicated remote context, do not inherit a
|
|
268
|
-
// workstation's historical
|
|
268
|
+
// workstation's historical directory allowlist. An optional
|
|
269
269
|
// MCP_GATEWAY_ALLOWED_ROOTS restriction is enforced by GatewayToolAdapter before
|
|
270
270
|
// calls reach this filesystem layer.
|
|
271
271
|
const remoteGatewayContext = process.env.MCP_DEVICE_REMOTE === 'true';
|
|
@@ -9,7 +9,7 @@ const isUrl = (source) => source.startsWith('http://') || source.startsWith('htt
|
|
|
9
9
|
let cachedChromePath = null; // null = not checked yet
|
|
10
10
|
let chromeCheckPromise = null;
|
|
11
11
|
/**
|
|
12
|
-
* Get
|
|
12
|
+
* Get the local engine's private Puppeteer cache directory.
|
|
13
13
|
*/
|
|
14
14
|
function getPuppeteerCacheDir() {
|
|
15
15
|
return join(dirname(CONFIG_FILE), 'puppeteer-cache');
|
package/dist/tools/schemas.d.ts
CHANGED
|
@@ -117,15 +117,15 @@ export declare const WriteFileArgsSchema: z.ZodObject<{
|
|
|
117
117
|
mode: z.ZodDefault<z.ZodEnum<["rewrite", "append"]>>;
|
|
118
118
|
origin: z.ZodOptional<z.ZodEnum<["ui", "llm"]>>;
|
|
119
119
|
}, "strip", z.ZodTypeAny, {
|
|
120
|
+
mode: "rewrite" | "append";
|
|
120
121
|
path: string;
|
|
121
122
|
content: string;
|
|
122
|
-
mode: "rewrite" | "append";
|
|
123
123
|
origin?: "ui" | "llm" | undefined;
|
|
124
124
|
}, {
|
|
125
125
|
path: string;
|
|
126
126
|
content: string;
|
|
127
|
-
origin?: "ui" | "llm" | undefined;
|
|
128
127
|
mode?: "rewrite" | "append" | undefined;
|
|
128
|
+
origin?: "ui" | "llm" | undefined;
|
|
129
129
|
}>;
|
|
130
130
|
export declare const PdfInsertOperationSchema: z.ZodObject<{
|
|
131
131
|
type: z.ZodLiteral<"insert">;
|
|
@@ -348,7 +348,6 @@ export declare const InteractWithProcessArgsSchema: z.ZodObject<{
|
|
|
348
348
|
wait_for_prompt?: boolean | undefined;
|
|
349
349
|
}>;
|
|
350
350
|
export declare const GetUsageStatsArgsSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
351
|
-
export declare const GiveFeedbackArgsSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
352
351
|
export declare const StartSearchArgsSchema: z.ZodObject<{
|
|
353
352
|
path: z.ZodString;
|
|
354
353
|
pattern: z.ZodString;
|
package/dist/tools/schemas.js
CHANGED
|
@@ -153,15 +153,6 @@ export const InteractWithProcessArgsSchema = z.object({
|
|
|
153
153
|
});
|
|
154
154
|
// Usage stats schema
|
|
155
155
|
export const GetUsageStatsArgsSchema = z.object({});
|
|
156
|
-
// Feedback tool schema - no pre-filled parameters, all user input
|
|
157
|
-
export const GiveFeedbackArgsSchema = z.object({
|
|
158
|
-
// No parameters needed - form will be filled manually by user
|
|
159
|
-
// Only auto-filled hidden fields remain:
|
|
160
|
-
// - tool_call_count (auto)
|
|
161
|
-
// - days_using (auto)
|
|
162
|
-
// - platform (auto)
|
|
163
|
-
// - client_id (auto)
|
|
164
|
-
});
|
|
165
156
|
// Search schemas (renamed for natural language)
|
|
166
157
|
export const StartSearchArgsSchema = z.object({
|
|
167
158
|
path: z.string(),
|
|
@@ -236,7 +227,6 @@ export const toolArgSchemas = {
|
|
|
236
227
|
kill_process: KillProcessArgsSchema,
|
|
237
228
|
get_usage_stats: GetUsageStatsArgsSchema,
|
|
238
229
|
get_recent_tool_calls: GetRecentToolCallsArgsSchema,
|
|
239
|
-
give_feedback_to_desktop_commander: GiveFeedbackArgsSchema,
|
|
240
230
|
get_prompts: GetPromptsArgsSchema,
|
|
241
231
|
track_ui_event: TrackUiEventArgsSchema,
|
|
242
232
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<head>
|
|
7
7
|
<meta charset="utf-8" />
|
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
9
|
-
<title>
|
|
9
|
+
<title>MCP Device File Preview</title>
|
|
10
10
|
<link rel="stylesheet" href="./styles.css" />
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
@@ -141,7 +141,7 @@ body.dc-ready {
|
|
|
141
141
|
|
|
142
142
|
|
|
143
143
|
/*
|
|
144
|
-
* App-specific styling for File Preview — tuned
|
|
144
|
+
* App-specific styling for File Preview — tuned for the MCP Device UI.
|
|
145
145
|
*/
|
|
146
146
|
:root {
|
|
147
147
|
--code-bg: var(--panel);
|
|
@@ -166,7 +166,7 @@ body.dc-ready {
|
|
|
166
166
|
--toc-rail-width: 320px;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
/* ── Panel (
|
|
169
|
+
/* ── Panel (compact card) ── */
|
|
170
170
|
|
|
171
171
|
.panel {
|
|
172
172
|
margin-top: 6px;
|
package/dist/utils/capture.d.ts
CHANGED
|
@@ -1,35 +1,12 @@
|
|
|
1
1
|
export declare function runInUiOriginCallContext<T>(fn: () => T): T;
|
|
2
2
|
export declare function isInsideUiOriginCall(): boolean;
|
|
3
|
-
/**
|
|
4
|
-
* Hard kill-switch for telemetry via environment variable.
|
|
5
|
-
*
|
|
6
|
-
* Independent of the persisted `telemetryEnabled` config so that tests, CI and
|
|
7
|
-
* one-off runs can suppress all analytics without mutating the user's config.
|
|
8
|
-
* Set DESKTOP_COMMANDER_DISABLE_TELEMETRY to 1/true/yes/on to disable.
|
|
9
|
-
*/
|
|
10
3
|
export declare function isTelemetryDisabledByEnv(): boolean;
|
|
11
|
-
/**
|
|
12
|
-
* Sanitizes error objects to remove potentially sensitive information like file paths
|
|
13
|
-
* @param error Error object or string to sanitize
|
|
14
|
-
* @returns An object with sanitized message and optional error code
|
|
15
|
-
*/
|
|
16
4
|
export declare function sanitizeError(error: any): {
|
|
17
5
|
message: string;
|
|
18
6
|
code?: string;
|
|
19
7
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
export declare const captureBase: (captureURL: string, event: string, properties?: any) => Promise<void>;
|
|
26
|
-
export declare const capture: (event: string, properties?: any) => Promise<void>;
|
|
27
|
-
export declare const capture_call_tool: (event: string, properties?: any) => Promise<void>;
|
|
28
|
-
export declare const capture_ui_event: (event: string, properties?: any) => Promise<void>;
|
|
29
|
-
/**
|
|
30
|
-
* Wrapper for capture() that automatically adds remote flag for device telemetry
|
|
31
|
-
* Also adds additional privacy filtering to remove sensitive identity information
|
|
32
|
-
* @param event Event name
|
|
33
|
-
* @param properties Optional event properties
|
|
34
|
-
*/
|
|
35
|
-
export declare const captureRemote: (event: string, properties?: any) => Promise<void>;
|
|
8
|
+
export declare const captureBase: (_captureURL: string, _event: string, _properties?: any) => Promise<void>;
|
|
9
|
+
export declare const capture: (_event: string, _properties?: any) => Promise<void>;
|
|
10
|
+
export declare const capture_call_tool: (_event: string, _properties?: any) => Promise<void>;
|
|
11
|
+
export declare const capture_ui_event: (_event: string, _properties?: any) => Promise<void>;
|
|
12
|
+
export declare const captureRemote: (_event: string, _properties?: any) => Promise<void>;
|