This is MCP server for Claude that gives it terminal control, file system search and diff file editing capabilities
This report summarizes scanner evidence for wonderwhy-er/DesktopCommanderMCP, a JavaScript project with 10,181 GitHub stars. SynthScan examined 99,734 lines of code across 457 source files, recording 321 pattern matches distributed across 10 syntactic categories. The legacy adjusted pattern signal is 3.9 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 305 finding(s).
Are imports declared and package/API names verified? 2 finding(s).
Are wrappers, guards, and repeated structures necessary? 252 finding(s).
Which changes need focused human review? 321 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 321 distinct pattern matches across 10 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | setup-claude-server.js | 265 | async function enhancedGetTrackingProperties(additionalProps = {}) { | CODE |
| LOW | uninstall-claude-server.js | 548 | async function removeDesktopCommanderConfig() { | CODE |
| LOW | test/test-markdown-preview.js | 130 | async function testWikiRewriteAndRendering() { | CODE |
| LOW | test/test-markdown-preview.js | 166 | async function testFailedSaveResyncsEditBaseline() { | CODE |
| LOW | test/test-markdown-preview.js | 310 | async function testSuccessfulSaveResetsUndoBaseline() { | CODE |
| LOW | test/test-markdown-preview.js | 375 | async function testFullscreenWorkspaceHelpers() { | CODE |
| LOW | test/test-markdown-preview.js | 401 | async function testCopyFormatsAndEditorShell() { | CODE |
| LOW | test/test-markdown-preview.js | 432 | async function testPartialDocumentBecomesNewEditBaseline() { | CODE |
| LOW | test/test-markdown-preview.js | 481 | async function testRefreshDoesNotMisclassifyMarkdownContentAsDeletion() { | CODE |
| LOW | test/test-markdown-preview.js | 525 | async function testUnsupportedRawContentPreview() { | CODE |
| LOW | test/test-literal-search.js | 153 | async function testLiteralSearchExactMatches() { | CODE |
| LOW | test/test-literal-search.js | 179 | async function testRegexVsLiteralDifference() { | CODE |
| LOW | test/test-literal-search.js | 208 | async function testSpecialCharactersLiteralSearch() { | CODE |
| LOW | test/test-literal-search.js | 297 | async function testRegexFailureLiteralSuccess() { | CODE |
| LOW | test/test-allowed-directories.js | 118 | async function testEmptyAllowedDirectories() { | CODE |
| LOW | test/test-allowed-directories.js | 147 | async function testSpecificAllowedDirectory() { | CODE |
| LOW | test/test-allowed-directories.js | 184 | async function testRootInAllowedDirectories() { | CODE |
| LOW | test/test-allowed-directories.js | 286 | async function testSpecificAllowedDirectoryWithSlash() { | CODE |
| LOW | test/test-process-memory.js | 36 | async function testFailedSamplingIsReported() { | CODE |
| LOW | test/test-svg-text.js | 55 | async function readMultipleFilesAnswersTheText(client, dir) { | CODE |
| LOW | test/test-svg-text.js | 88 | async function previewWidgetStillGetsTheUrlImage(client) { | CODE |
| LOW | test/test-svg-text.js | 100 | async function previewWidgetStillGetsTheImage(client, dir) { | CODE |
| LOW | test/test-startup-imports.js | 48 | async function testNothingLoadedBeforeInitialize(server) { | CODE |
| LOW | test/test-startup-imports.js | 59 | async function testLoadedSoonAfterInitialize(server) { | CODE |
| LOW | test/test-startup-imports.js | 94 | async function testCallWhileLoadingAnswersAtOnce(server, file) { | CODE |
| LOW | test/test-startup-imports.js | 112 | async function testReadMultipleFilesWhileLoading(server, file, second) { | CODE |
| LOW | test/test-startup-imports.js | 123 | async function testWritePdfMarkdownStartingWithLink(server, dir) { | CODE |
| LOW | test/test-startup-imports.js | 135 | async function testSameCallWorksOnceLoaded(server, file) { | CODE |
| LOW | test/test-startup-imports.js | 156 | async function testFailedRequireTriesAgain(server, file) { | CODE |
| LOW | test/test-startup-imports.js | 170 | async function testFailedImportSaysRestart(server, file) { | CODE |
| LOW | test/test-default-shell.js | 64 | function testDefaultShellDetection(availableShells) { | CODE |
| LOW | test/test-default-shell.js | 111 | async function testConfigurationPersistence(availableShells) { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 54 | async function testPipeTableSurvivesRoundTrip() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 78 | async function testAdjacentHeadingsKeepOriginalSpacing() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 90 | async function testWikilinkSurvivesRoundTrip() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 102 | async function testTrailingNewlineSurvives() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 114 | async function testCombinedBugReportFile() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 127 | async function testYamlFrontmatterSurvives() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 139 | async function testSquareBracketsNotEscaped() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 151 | async function testUnderscoresNotEscaped() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 206 | async function testReadmeStyleFileNotCollapsed() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 265 | async function testTableInsideRealisticDoc() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 322 | async function testEmojiPrefixedSoftBreaksRestored() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 341 | async function testLinkInTableCellSurvivesRoundTrip() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 360 | async function testStarBulletMarkerPreserved() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 378 | async function testRelativePathLinksSurvive() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 402 | async function testLessThanInProseNotEscaped() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 428 | async function testTrailingHardBreakWhitespacePreserved() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 448 | async function testBoldAroundInlineCodePreserved() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 473 | async function testEscapedPipeInTableCellPreserved() { | CODE |
| LOW | test/test-markdown-editor-roundtrip.js | 496 | async function testListItemWithContinuationLine() { | CODE |
| LOW | test/test-pdf-render-resources.js | 246 | async function testCallerArgsCannotChangeProfile() { | CODE |
| LOW | test/test-session-blocked.js | 20 | async function testRunningProcessNotBlocked() { | CODE |
| LOW | test/test-edit-block-line-endings.js | 259 | async function testContextAwareReplacement() { | CODE |
| LOW | test/test-edit-block-line-endings.js | 429 | async function runEditBlockLineEndingTests() { | CODE |
| LOW | test/test-feature-flags-timeout.js | 205 | async function testRefreshFetchIgnoresAbort() { | CODE |
| LOW | test/test-feature-flags-timeout.js | 275 | async function reportPlatformDiagnostics() { | CODE |
| LOW | test/test-process-wait-config.js | 32 | async function testCeilingUnderClientTimeout() { | CODE |
| LOW | test/test-process-wait-config.js | 137 | async function testNodeLocalWaitsWithinTheCeiling() { | CODE |
| LOW | test/test-pdf-render-options.js | 178 | async function testFrontMatterDestOutsideAllowedDirs() { | CODE |
| 178 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/utils/files/docx.ts | 25 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 27 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 65 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 67 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 109 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 111 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 248 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 250 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 453 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 455 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 504 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 506 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 518 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| MEDIUM | src/utils/files/docx.ts | 520 | // ════════════════════════════════════════════════════════════════ | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | test/test-remote-channel-reconnect.js | 821 | await withQuietLogs(() => | COMMENT |
| LOW | test/repro/test-exit-after-fetch.js | 1 | // Repro: the product's own exit paths abort the process on Windows when they | COMMENT |
| LOW | test/repro/test-config-damaged-start.js | 1 | // Repro (#692): with a damaged ~/.claude-server-commander/config.json, | COMMENT |
| LOW | test/repro/test-offline-update-exit.js | 1 | // Repro: the remote device's shutdown script (blocking-offline-update.js) | COMMENT |
| LOW | test/repro/test-pdf-launch-failure-server.js | 1 | // Server-level repro: one failed Chrome launch in write_pdf must not take the | COMMENT |
| LOW | test/repro/test-config-lock-frozen-holder.js | 1 | // Repro: a Desktop Commander process frozen for over 30 s while it holds the | COMMENT |
| LOW | test/repro/test-read-abort-frees-thread.js | 1 | // Demonstration: runWithAbortableTimeout actually CANCELS the underlying read | COMMENT |
| LOW | test/repro/test-search-child-config-lock.js | 1 | // Repro (#768): in a full test run, test-search-without-ripgrep.js took 60.8 s | COMMENT |
| LOW | test/repro/test-release-packages-ship-contract.js | 1 | // Repro: neither release package carries #794's telemetry contract, so the | COMMENT |
| LOW | test/repro/test-release-packages-ship-contract.js | 21 | // files, not a link. | COMMENT |
| LOW | test/repro/test-startup-heavy-imports.js | 1 | // Repro (#715): Desktop Commander takes 25-90 s to answer the MCP `initialize` | COMMENT |
| LOW | test/repro/test-withtimeout-leak.js | 1 | // Repro: withTimeout() rejects on schedule but does NOT cancel the underlying | COMMENT |
| LOW | test/repro/test-config-old-writer.js | 1 | // Repro (#697): `desktop-commander remote` beside an older Desktop Commander | COMMENT |
| LOW | test/repro/test-search-memory.js | 1 | // Repro (#716): a content search with maxResults: 200 on a large project took | COMMENT |
| LOW | test/repro/test-search-memory.js | 21 | // Measured on the base (4715bd4), default sizes, over 4 runs each on Windows 11 | COMMENT |
| LOW | test/repro/test-search-memory.js | 41 | // REPRO_V8_LIMIT=1 adds the 540 MB line: set REPRO_TIMEOUT_MS=2400000 and | COMMENT |
| LOW | test/repro/test-interact-join-stall.js | 1 | // Repro (v2): interact_with_process re-join()s the WHOLE buffer every 50ms poll | COMMENT |
| LOW | .github/workflows/release.yml | 1 | # Release pipeline for Desktop Commander. | COMMENT |
| LOW | .github/workflows/release.yml | 21 | # writes to origin. Everything before that push happens in the | COMMENT |
| LOW | .github/workflows/release.yml | 41 | # Use it to validate workflow changes end-to-end before a real release — | COMMENT |
| LOW | src/terminal-manager.ts | 181 | COMMENT | |
| LOW | src/ui/file-preview/src/markdown/editor.ts | 61 | * restoration converts back to the literal `\|` in the output. */ | COMMENT |
| LOW | src/ui/file-preview/src/markdown/editor.ts | 221 | // restore — same shape, different schema-level reason for needing it. | COMMENT |
| LOW | src/ui/file-preview/src/markdown/editor.ts | 241 | // Tiptap's HTML output path HTML-escapes bare `<` characters in | COMMENT |
| LOW | src/ui/file-preview/src/markdown/editor.ts | 261 | // bullet is emitted as `- `. If the source used `*` (or a mix), we'd | COMMENT |
| LOW | src/ui/file-preview/src/markdown/editor.ts | 501 | // Quick check: if no candidate escapes are even present in this | COMMENT |
| LOW | src/tools/edit.ts | 301 | // | COMMENT |
| LOW | src/remote-device/desktop-commander-integration.ts | 121 | } | COMMENT |
| LOW | src/remote-device/desktop-commander-integration.ts | 141 | // transport.onclose/onerror with its own handlers and the SDK states | COMMENT |
| LOW | src/remote-device/remote-channel.ts | 81 | // Cap on a recreate's rebuild step so a hung await can't disable the watchdog. | COMMENT |
| LOW | src/remote-device/remote-channel.ts | 101 | const TOKEN_REFRESH_INTERVAL_MS = 45 * 60 * 1000; | COMMENT |
| LOW | src/remote-device/remote-channel.ts | 161 | return Math.max(1_000, Math.round(stepMs * Math.max(0, Math.min(1, random())))); | COMMENT |
| LOW | src/remote-device/remote-channel.ts | 1201 | // otherwise realtime-js never tears the socket down and a | COMMENT |
| LOW | src/remote-device/remote-channel.ts | 1381 | // This write asserts status:'online' too, so it MUST respect the | COMMENT |
| LOW | src/remote-device/remote-channel.ts | 1601 | // races that matter: a heartbeat tick firing as the signal arrives, and | COMMENT |
| LOW | src/remote-device/device.ts | 281 | // rejection means "running, but nothing can reach this device", and | COMMENT |
| LOW | src/remote-device/device.ts | 621 | // to THIS SAME PROCESS, so an in-memory check is sufficient and, unlike | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | test-listener-bug.js | 97 | // Step 1: Start Node.js REPL | COMMENT |
| LOW⚡ | test-listener-bug.js | 107 | // Step 2: Send first command | COMMENT |
| LOW⚡ | test-listener-bug.js | 117 | // Step 3: First read_process_output (this might break listeners with removeAllListeners) | COMMENT |
| LOW⚡ | test-listener-bug.js | 126 | // Step 4: Send second command WITHOUT waiting (so output stays in buffer) | COMMENT |
| LOW | test-listener-bug.js | 139 | // Step 5: Second read_process_output (THIS WILL FAIL if listeners were removed) | COMMENT |
| LOW | scripts/validate-tools-sync.js | 70 | // Step 1: Send initialize request | COMMENT |
| LOW | scripts/validate-tools-sync.js | 89 | // Step 2: Send tools/list request | COMMENT |
| LOW | src/tools/filesystem.ts | 149 | * and we need to check if any of the parent directories exist | COMMENT |
| LOW⚡ | src/utils/dockerPrompt.ts | 13 | // Step 1: Check if client is Docker MCP Gateway | COMMENT |
| LOW⚡ | src/utils/dockerPrompt.ts | 17 | // Step 2: Check if this is their first or second command (0 or 1 total calls) | COMMENT |
| LOW⚡ | src/remote-device/device-authenticator.ts | 62 | // Step 1: Request device code | COMMENT |
| LOW⚡ | src/remote-device/device-authenticator.ts | 65 | // Step 2: Display user instructions and open browser | COMMENT |
| LOW⚡ | src/remote-device/device-authenticator.ts | 68 | // Step 3: Poll for authorization | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | test-listener-bug.js | 97 | // Step 1: Start Node.js REPL | COMMENT |
| LOW⚡ | test-listener-bug.js | 107 | // Step 2: Send first command | COMMENT |
| LOW⚡ | test-listener-bug.js | 117 | // Step 3: First read_process_output (this might break listeners with removeAllListeners) | COMMENT |
| LOW⚡ | test-listener-bug.js | 126 | // Step 4: Send second command WITHOUT waiting (so output stays in buffer) | COMMENT |
| LOW | test-listener-bug.js | 139 | // Step 5: Second read_process_output (THIS WILL FAIL if listeners were removed) | COMMENT |
| LOW | scripts/validate-tools-sync.js | 70 | // Step 1: Send initialize request | COMMENT |
| LOW | scripts/validate-tools-sync.js | 89 | // Step 2: Send tools/list request | COMMENT |
| LOW⚡ | src/utils/dockerPrompt.ts | 13 | // Step 1: Check if client is Docker MCP Gateway | COMMENT |
| LOW⚡ | src/utils/dockerPrompt.ts | 17 | // Step 2: Check if this is their first or second command (0 or 1 total calls) | COMMENT |
| LOW⚡ | src/remote-device/device-authenticator.ts | 62 | // Step 1: Request device code | COMMENT |
| LOW⚡ | src/remote-device/device-authenticator.ts | 65 | // Step 2: Display user instructions and open browser | COMMENT |
| LOW⚡ | src/remote-device/device-authenticator.ts | 68 | // Step 3: Poll for authorization | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | test/test-enhanced-repl.js | 88 | print(greet(f"Guest {i+1}"))`; | CODE |
| HIGH | test/test-search-code.js | 95 | print("This is a python function") | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | src/tools/pdf/extract-images.ts | 102 | // Ignore conversion errors as requested | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | install.sh | 16 | # Check if Node.js is installed | COMMENT |
| LOW | install-docker.sh | 79 | # Check if Docker is available | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/utils/capture.ts | 294 | // Is MCP running in a container - use robust detection | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/command-manager.ts | 161 | // Don't forget to add the last command | COMMENT |