host-execution-mcp 1.0.0

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.
Files changed (126) hide show
  1. package/CHANGELOG.md +330 -0
  2. package/LICENSE +21 -0
  3. package/README.md +92 -0
  4. package/SECURITY.md +72 -0
  5. package/dist/access/docker-runtime.d.ts +38 -0
  6. package/dist/access/docker-runtime.js +61 -0
  7. package/dist/access/docker-runtime.js.map +1 -0
  8. package/dist/access/index.d.ts +110 -0
  9. package/dist/access/index.js +174 -0
  10. package/dist/access/index.js.map +1 -0
  11. package/dist/access/paradi-auth.d.ts +33 -0
  12. package/dist/access/paradi-auth.js +89 -0
  13. package/dist/access/paradi-auth.js.map +1 -0
  14. package/dist/access/paradi-transport.d.ts +10 -0
  15. package/dist/access/paradi-transport.js +39 -0
  16. package/dist/access/paradi-transport.js.map +1 -0
  17. package/dist/access/paradi-username.d.ts +5 -0
  18. package/dist/access/paradi-username.js +14 -0
  19. package/dist/access/paradi-username.js.map +1 -0
  20. package/dist/access/probe.d.ts +14 -0
  21. package/dist/access/probe.js +63 -0
  22. package/dist/access/probe.js.map +1 -0
  23. package/dist/access/session-pool.d.ts +25 -0
  24. package/dist/access/session-pool.js +46 -0
  25. package/dist/access/session-pool.js.map +1 -0
  26. package/dist/access/ssh-transport.d.ts +16 -0
  27. package/dist/access/ssh-transport.js +74 -0
  28. package/dist/access/ssh-transport.js.map +1 -0
  29. package/dist/access/types.d.ts +43 -0
  30. package/dist/access/types.js +21 -0
  31. package/dist/access/types.js.map +1 -0
  32. package/dist/config.d.ts +52 -0
  33. package/dist/config.js +179 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.js +19 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/instructions.d.ts +4 -0
  39. package/dist/instructions.js +93 -0
  40. package/dist/instructions.js.map +1 -0
  41. package/dist/kubectl/runner.d.ts +32 -0
  42. package/dist/kubectl/runner.js +79 -0
  43. package/dist/kubectl/runner.js.map +1 -0
  44. package/dist/server.d.ts +3 -0
  45. package/dist/server.js +24 -0
  46. package/dist/server.js.map +1 -0
  47. package/dist/ssh/access-client.d.ts +62 -0
  48. package/dist/ssh/access-client.js +470 -0
  49. package/dist/ssh/access-client.js.map +1 -0
  50. package/dist/ssh/access-errors.d.ts +53 -0
  51. package/dist/ssh/access-errors.js +180 -0
  52. package/dist/ssh/access-errors.js.map +1 -0
  53. package/dist/ssh/access-sftp.d.ts +14 -0
  54. package/dist/ssh/access-sftp.js +456 -0
  55. package/dist/ssh/access-sftp.js.map +1 -0
  56. package/dist/ssh/bastion-client.d.ts +60 -0
  57. package/dist/ssh/bastion-client.js +423 -0
  58. package/dist/ssh/bastion-client.js.map +1 -0
  59. package/dist/ssh/bastion-errors.d.ts +62 -0
  60. package/dist/ssh/bastion-errors.js +190 -0
  61. package/dist/ssh/bastion-errors.js.map +1 -0
  62. package/dist/ssh/hop1-sftp.d.ts +14 -0
  63. package/dist/ssh/hop1-sftp.js +437 -0
  64. package/dist/ssh/hop1-sftp.js.map +1 -0
  65. package/dist/tools/copy-job-schemas.d.ts +709 -0
  66. package/dist/tools/copy-job-schemas.js +48 -0
  67. package/dist/tools/copy-job-schemas.js.map +1 -0
  68. package/dist/tools/register.d.ts +14 -0
  69. package/dist/tools/register.js +619 -0
  70. package/dist/tools/register.js.map +1 -0
  71. package/dist/tools/remote-schemas.d.ts +879 -0
  72. package/dist/tools/remote-schemas.js +214 -0
  73. package/dist/tools/remote-schemas.js.map +1 -0
  74. package/dist/utils/copy-executor.d.ts +41 -0
  75. package/dist/utils/copy-executor.js +231 -0
  76. package/dist/utils/copy-executor.js.map +1 -0
  77. package/dist/utils/copy-job-manager.d.ts +34 -0
  78. package/dist/utils/copy-job-manager.js +400 -0
  79. package/dist/utils/copy-job-manager.js.map +1 -0
  80. package/dist/utils/copy-job-store.d.ts +18 -0
  81. package/dist/utils/copy-job-store.js +104 -0
  82. package/dist/utils/copy-job-store.js.map +1 -0
  83. package/dist/utils/copy-job-types.d.ts +133 -0
  84. package/dist/utils/copy-job-types.js +37 -0
  85. package/dist/utils/copy-job-types.js.map +1 -0
  86. package/dist/utils/copy-transfer.d.ts +59 -0
  87. package/dist/utils/copy-transfer.js +86 -0
  88. package/dist/utils/copy-transfer.js.map +1 -0
  89. package/dist/utils/remote-copy.d.ts +14 -0
  90. package/dist/utils/remote-copy.js +115 -0
  91. package/dist/utils/remote-copy.js.map +1 -0
  92. package/dist/utils/remote-executor.d.ts +15 -0
  93. package/dist/utils/remote-executor.js +34 -0
  94. package/dist/utils/remote-executor.js.map +1 -0
  95. package/dist/utils/remote-target.d.ts +22 -0
  96. package/dist/utils/remote-target.js +75 -0
  97. package/dist/utils/remote-target.js.map +1 -0
  98. package/dist/utils/result.d.ts +87 -0
  99. package/dist/utils/result.js +81 -0
  100. package/dist/utils/result.js.map +1 -0
  101. package/dist/utils/shell-quote.d.ts +22 -0
  102. package/dist/utils/shell-quote.js +55 -0
  103. package/dist/utils/shell-quote.js.map +1 -0
  104. package/dist/utils/timeout.d.ts +1 -0
  105. package/dist/utils/timeout.js +18 -0
  106. package/dist/utils/timeout.js.map +1 -0
  107. package/dist/utils/truncate.d.ts +5 -0
  108. package/dist/utils/truncate.js +12 -0
  109. package/dist/utils/truncate.js.map +1 -0
  110. package/dist/version.d.ts +1 -0
  111. package/dist/version.js +7 -0
  112. package/dist/version.js.map +1 -0
  113. package/docs/AGENT_GUIDE.md +161 -0
  114. package/docs/CONFIGURATION.md +130 -0
  115. package/docs/COPY_SPECIALIZED_TEST_CHECKLIST.md +9 -0
  116. package/docs/HOP1_E2E_CHECKLIST.md +128 -0
  117. package/docs/HOP1_E2E_RESULTS.json +647 -0
  118. package/docs/PARADI_ACCESS_PLAN.md +272 -0
  119. package/docs/REMOTE_COPY_JOB_API.md +92 -0
  120. package/docs/TODO.md +66 -0
  121. package/docs/USER_GUIDE.zh-CN.md +93 -0
  122. package/docs/clients/claude-desktop.json.example +12 -0
  123. package/docs/clients/cursor-npx.json.example +12 -0
  124. package/docs/clients/cursor.json.example +12 -0
  125. package/docs/clients/vscode.json.example +12 -0
  126. package/package.json +65 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,330 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] - 2026-07-27
11
+
12
+ First public release of **`host-execution-mcp`** (new npm package name; continues the Access → HostExecutionContext → Runtime line formerly developed as `bastion-k8s-mcp`).
13
+
14
+ ### Added
15
+
16
+ - Access shapes: `ACCESS_KIND=ssh` (landing / jump+`TARGET_*`) and `ACCESS_KIND=paradi-gateway`
17
+ - Paradi `ACCESS_PARADI_AUTH=auto|slash|compound` (default `auto`): try slash then compound on AUTH_FAILED; pin winner for the MCP process lifetime
18
+ - `access_check` may include `paradiAuth` after a successful Paradi connect
19
+ - Tools: `access_check`, `shell_exec`, `context_derive` / `context_probe`, `k8s_*`, `docker_*`, `remote_*` / `remote_copy_*`
20
+
21
+ ### Notes
22
+
23
+ - Install: `npx -y host-execution-mcp@1.0.0` (or `@latest`)
24
+ - Env: `ACCESS_*` / `TARGET_*` only (no `BASTION_*` / `REMOTE_SSH_*` aliases)
25
+
26
+ ## [6.0.0] - 2026-07-23
27
+
28
+ ### Breaking
29
+
30
+ - Package / MCP server rename: `bastion-k8s-mcp` → `host-execution-mcp`
31
+ - Source / index paths: `packages/mcp/bastion-k8s` → `packages/mcp/host-execution`, `mcp/bastion-k8s` → `mcp/host-execution`
32
+ - Internal rename: `BastionClient` → `AccessClient` (`access-client.ts` / `access-errors.ts` / `access-sftp.ts`)
33
+ - Removed CLI bin alias `bastion-k8s-mcp` (bin: `host-execution-mcp` only)
34
+
35
+ ### Changed
36
+
37
+ - Paradi Zod fail-fast: `remote_*` / `remote_copy_*` schemas omit `target=context`; shell/docker omit `contextKey` under `ACCESS_KIND=paradi-gateway`
38
+ - Copy guard rejects Paradi `target=context` (and `host`) before materialize
39
+ - Agent-facing tool descriptions drop Hop1/Hop2 jargon; docs aligned to ACCESS/Context naming + 5.1 backlog
40
+
41
+ ## [5.0.0] - 2026-07-23
42
+
43
+ ### Breaking
44
+
45
+ - Tool renames: `bastion_check` → `access_check`, `bastion_exec` → `shell_exec` (optional `contextKey`)
46
+ - New tool: `context_derive` (returns `contextKey`; unavailable under `ACCESS_KIND=paradi-gateway`)
47
+ - Error rename: `BASTION_UNAVAILABLE` → `ACCESS_UNAVAILABLE`; `BastionUnavailableError` → `AccessUnavailableError`
48
+ - Remote schemas: `target=bastion` → `target=landing`; removed agent-facing `target=host`; added `target=context` with `contextKey`
49
+ - `docker_*` / `context_probe`: `host`/`useTarget` removed — use `contextKey` only (omit for landing)
50
+ - `ssh_exec` response includes `contextKey` + command result fields
51
+ - Removed env alias `BASTION_COPY_JOB_ROOT` — use `ACCESS_COPY_JOB_ROOT` only
52
+ - Removed deprecated `shellOnAccess` alias on `AccessRuntime`
53
+
54
+ ### Changed
55
+
56
+ - Copy engine internal target `bastion` → `landing`; `target=context` materializes in copy handlers before execute
57
+ - Instructions / AGENT_GUIDE / README tool table aligned to 5.0.0 naming (no deferred renames)
58
+ - Instructions: ssh_exec = reuse-only derive+shell shortcut; shell_exec never creates Context; omit contextKey = AccessTransport landing; remote target=context defined; Paradi derive soft-worded; k8s_* notes contextKey not exposed yet; drop redundant Prefer-ssh_exec / Host-level duplication
59
+ - Instructions (MMR consensus): ssh_exec = plain derive+shell shortcut; Paradi derive = unsupported by transport; k8s_* landing-only by design; slim system prompt (cookbook → AGENT_GUIDE)
60
+
61
+ ## [4.0.0] - 2026-07-22
62
+
63
+ ### Breaking
64
+
65
+ - Removed env aliases: `BASTION_HOST`/`USER`/`PORT`/`PASSWORD`/`PRIVATE_KEY_PATH`, `REMOTE_SSH_USER`/`PASSWORD`. Use `ACCESS_*` / `TARGET_*` only; leftover old names throw with rename hints.
66
+ - Policy: prefer `ACCESS_POLICY_ALLOWED_NAMESPACES` (`ALLOWED_NAMESPACES` still accepted as policy-only short name).
67
+
68
+ ### Changed
69
+
70
+ - Docs / README / SECURITY / `.env.example` aligned to ACCESS_* only
71
+ - Phase 4 cleanup complete per PARADI_ACCESS_PLAN
72
+ - Post-review hardening: Paradi-aware MCP instructions & tool descriptions; forbid `remote_copy` `target=host` under Paradi (sync + async start); derived `cwd` uses bash single-quote; derive context key hashes password; `ACCESS_COPY_JOB_ROOT`; probe exitCode handling; tool-description UTF-8 arrows; `remote_read`/`remote_ps`/`remote_copy_start` Paradi notes; SECURITY.md 4.0
73
+ - Instructions converge on Access/Runtime/derive (drop Hop1/Hop2); `current Context` wording; honest `docker_*(host/useTarget)` = derive convenience; hide Paradi username encoding; rename to `buildDerivedHostAuthGuidance` (no Hop2 alias); remove static `SERVER_INSTRUCTIONS`
74
+ - Remove unused internals: `AccessRuntime.deriveToHost` / `shellOnContext`, `remoteHop2FilePathEndpointSchema`, unused `remoteCopy*ResultSchema`
75
+ - Instruction v3 (text): Access-layer selectors vs Runtime; host-entry split; no Legacy wording; K8s probe on landing Context; Never reuse ACCESS_USER; Paradi “not available under this ACCESS_KIND” (not forever-disabled); capabilities not shared across Contexts
76
+
77
+ ## [3.3.0] - 2026-07-22
78
+
79
+ ### Added
80
+
81
+ - `ACCESS_KIND=paradi-gateway`: ParadiAccessTransport → landing Context on TARGET; Hop1 SSH username `ACCESS_USER/TARGET_HOST/TARGET_USER`
82
+ - Declared capabilities: `sftp: "degraded"`, `channelOpenSensitive: true` (preserved across probe)
83
+ - `ssh_exec` / derive / `useTarget` rejected under paradi (already on TARGET)
84
+
85
+ ### Changed
86
+
87
+ - `AccessClient` / Hop1 SFTP use `resolveAccessSshIdentity` (paradi-aware username)
88
+ - CONFIGURATION documents Paradi shape; `TARGET_PASSWORD` ignored for paradi auth
89
+
90
+ ## [3.2.0] - 2026-07-22
91
+
92
+ ### Added
93
+
94
+ - `context_probe`: probe Context capabilities (`docker` / `kubectl` CLI); tri-state true|false|unknown
95
+ - `docker_ps` / `docker_exec` / `docker_logs`: Docker Runtime via SessionPool (ACCESS default; `host` / `useTarget` to derive)
96
+ - Auto-probe `capabilities.docker` before docker_* when still unknown; fail if false
97
+
98
+ ### Changed
99
+
100
+ - Server instructions: unknown ≠ true; docker_* vs k8s_exec selection
101
+
102
+ ## [3.1.0] - 2026-07-22
103
+
104
+ ### Breaking
105
+
106
+ - Prefer `ACCESS_*` / `TARGET_*` env names (see CONFIGURATION.md). Legacy `BASTION_*` / `REMOTE_SSH_*` still accepted as temporary aliases.
107
+ - `TARGET_HOST` requires `TARGET_USER` (no fallback to `ACCESS_USER`).
108
+ - `ACCESS_KIND=paradi-gateway` rejected until Phase 3.
109
+
110
+ ### Added
111
+
112
+ - Access layer: `HostExecutionContext` (no session) + `SessionPool` + `SshAccessTransport`
113
+ - `ssh_exec` ≡ `deriveContext` + shell via `AccessRuntime` (optional host uses `TARGET_HOST`)
114
+ - Capabilities: `exec` + `docker`/`kubectl` = `"unknown"` until probe (Phase 2)
115
+
116
+ ### Changed
117
+
118
+ - Config / instructions / client examples document three Access shapes; Runtime not in mcp.json
119
+
120
+ ## [3.0.4] - 2026-07-22
121
+
122
+ ### Fixed
123
+
124
+ - Hop2 auth guidance: password-only (`REMOTE_SSH_PASSWORD` without `REMOTE_SSH_USER`) now says omit password but **still pass user** (was incorrectly "omit user/password")
125
+
126
+ ### Changed
127
+
128
+ - Slim Hop2 `REMOTE_SSH_*` agent guidance; drop redundant `bastion_check.remoteHostAuth` and `ssh_exec` Auth prose (server instructions + short schema remain)
129
+ - First-session handshake: `kubectl cluster-info` only for Kubernetes ops
130
+ - Clarify `k8s_logs` as stdout+stderr log stream; copy `skipIfComplete`/`verify`/`totalTimeoutMs` as all-hop copy rules; `bastion_check` hints in JSON message (other tools' `BASTION_UNAVAILABLE` hints in stderr)
131
+
132
+ ## [3.0.3] - 2026-07-22
133
+
134
+ ### Fixed
135
+
136
+ - Agent guidance: when `REMOTE_SSH_USER` / `REMOTE_SSH_PASSWORD` are set in MCP env, server instructions and `ssh_exec` descriptions now say defaults are **active** — omit credentials and **do not ask the human** (runtime already applied env; agents were still prompted by old "provide user" wording)
137
+
138
+ ### Changed
139
+
140
+ - `buildServerInstructions(config)` injects Hop2 auth status at startup (exposes default username, not password)
141
+ - `bastion_check` returns `remoteHostAuth` (`userConfigured`, `user`, `passwordConfigured`, `hint`)
142
+ - `hostFields` / `ssh_exec` parameter describes: omit when env defaults exist
143
+
144
+ ## [3.0.2] - 2026-07-21
145
+
146
+ ### Fixed
147
+
148
+ - `buildSshRemoteCommand`: quote the entire `bash -lc '<cmd>'` as one ssh remote argv so OpenSSH space-join no longer truncates multi-word commands (`ssh_exec`, `remote_*` target=host)
149
+ - `remote-copy` host post-copy `stat`: use `buildSshRemoteCommand` and pass the same password as scp (password-only hosts no longer fall back to BatchMode ssh)
150
+
151
+ ### Changed
152
+
153
+ - Shared Hop2 host transport prefix (`buildHostTransportPrefix` / `hostSshBaseOpts`) for ssh and scp so `-o` / sshpass opts do not fork
154
+ - Agent instructions + `AGENT_GUIDE`: shell metacharacters, non-TTY docker empty stdout, CRLF, `DOCKER_CONFIG`, `success=false` reads stdout; deferred `registry_check` noted in `TODO.md`
155
+
156
+ ### Tests
157
+
158
+ - `shell-quote`: multi-word command, URL `&`, sshpass, double-wrap (`buildBashLcCommand(buildSshRemoteCommand(...))`)
159
+
160
+ ## [3.0.1] - 2026-07-17
161
+
162
+ ### Added
163
+
164
+ - `BastionUnavailableError` (`code=BASTION_UNAVAILABLE`) with Chinese hints for VPN/network/auth failures
165
+ - `bastion_check` tool: short-timeout Local→Bastion SSH probe (≤10s), structured `{ reachable, latencyMs, ... }`
166
+ - `ensureAccessReady()` with positive/negative TTL on `exec` and Hop1 `uploadPath`/`downloadPath`
167
+
168
+ ### Changed
169
+
170
+ - Network/auth **connect-phase** failures fail-fast (no 3× reconnect); stream-phase `ECONNRESET`/`EPIPE` remain briefly retryable
171
+ - Probe/`bastion_check` use ephemeral SSH (do not sever in-flight exec/copy); transfer errors are not reclassified as bastion-down
172
+ - Auth classification tightened to ssh2 auth phrases (file `Permission denied` excluded)
173
+ - Server instructions / tool-selection prompt / descriptions: prefer `bastion_check` first; stop retrying on `BASTION_UNAVAILABLE`
174
+
175
+ ## [3.0.0] - 2026-07-16
176
+
177
+ ### Added
178
+
179
+ - Hop1 directory copy (local↔bastion): walk + per-file SFTP via `ssh2-sftp-client` (independent TCP from exec)
180
+ - `uploadPath` / `downloadPath` on AccessClient (file or directory)
181
+ - `skipIfComplete` + result fields `skippedBytes` / `skipped` / `skipReason` (`size-match` | `hash-match`)
182
+ - `resolveEffectiveVerify`: rejects `skipIfComplete` + `verify=none`
183
+ - Job status `timeout`; `connectionTimeoutMs` for Hop1 SFTP connect
184
+
185
+ ### Changed
186
+
187
+ - **BREAKING**: Removed Hop1 self-built stream engine and `.copy-meta.json` byte-offset resume
188
+ - **BREAKING**: Removed `resume` (use `skipIfComplete`); removed `idleTimeoutMs`; renamed `resumedFrom` → `skippedBytes`
189
+ - `remote_copy_file` remains sync-only; never auto-creates async jobs
190
+ - Skip gate strength is explicitly `verify` (no auto-upgrade size→sha256)
191
+
192
+ ### Fixed
193
+
194
+ - Hop1 upload `skipIfComplete`: verify fail after size match falls through to rewrite (aligned with download)
195
+ - Directory skip accumulates `skippedBytes`; directory supports per-file `verify=size+sha256`
196
+
197
+ ### Removed
198
+
199
+ - Stream upload/download, copy-meta helpers, `uploadFile`/`downloadFile` aliases, `resume` / `idleTimeoutMs` API surface
200
+
201
+ ## [2.2.1] - 2026-07-16
202
+
203
+ ### Fixed
204
+
205
+ - Job terminal-state fence: cancel cannot overwrite success/failed; success cannot overwrite cancelled
206
+ - Orphan reconcile on MCP start: leftover `queued|running` jobs → `failed` ("MCP restarted…")
207
+ - Hop1 stream abort no longer `disconnect()`s the shared SSH client (avoids killing concurrent tools/jobs)
208
+ - `exec` accepts `AbortSignal` and closes the channel (no 3× retry on abort/timeout)
209
+ - Pipeline copies share one wall-clock `totalTimeoutMs` budget across hops; abort checked between hops
210
+ - Upload fastPut / size-shortcut now run real verify; `verified: true` is no longer claimed without checks
211
+ - Resume: same-size local without matching `.copy-meta.json` forces restart (no silent skip)
212
+ - Stream idle timeout based on byte progress (+ write drain), not only read `data` events
213
+ - Pre-aborted signal no longer hits `totalTimer` TDZ `ReferenceError`
214
+ - Schema: `remote_copy_status` only accepts async `remote_copy_start` transferIds
215
+
216
+ ### Changed
217
+
218
+ - Cancel messaging documents Hop2 best-effort abort
219
+ - **Upload resume simplified**: no bastion-side meta / byte-offset append; `resume` only skips rewrite when remote size already matches + verify
220
+ - Unified `withSftp` + single `verifyTransfer` for Hop1; removed unused `detach` and numeric timeout overload on upload/download
221
+ - `throwIfAborted` lives in `copy-transfer` (shared)
222
+
223
+ ## [2.2.0] - 2026-07-16
224
+
225
+ ### Added
226
+
227
+ - **Async copy jobs (M2)**: `remote_copy_start` / `remote_copy_status` / `remote_copy_cancel`
228
+ - Job persistence under `~/.agent-compose/remote-copy/jobs/<transferId>.json` (override with `BASTION_COPY_JOB_ROOT`)
229
+ - In-process worker runs the same Copy Engine as sync `remote_copy_file`; cancel via `AbortSignal` (Hop1 streams aborted; shared SSH kept)
230
+ - Policy helpers: `shouldPreferAsyncCopy` (size>256MB or ETA>2min)
231
+ - Docs: `docs/REMOTE_COPY_JOB_API.md` marked implemented
232
+
233
+ ### Changed
234
+
235
+ - Server instructions / prompts recommend async tools for large or long-ETA copies
236
+ - Hop1 stream upload/download honor `signal` for cooperative abort
237
+
238
+ ## [2.1.0] - 2026-07-16
239
+
240
+ ### Added
241
+
242
+ - **`remote_copy_file` Hop1 SFTP resume**: optional `resume` with sibling `dest.copy-meta.json` (remote path/size/mtime) to prevent silent corruption
243
+ - **Idle + total timeouts**: `idleTimeoutMs` (default 60s, resets on data) and `totalTimeoutMs` (wall clock); legacy `timeoutMs` maps to `totalTimeoutMs`
244
+ - **Throttled progress**: `progressIntervalMs` (default 5s) + MCP `notifications/progress` when client provides `progressToken`
245
+ - **Result fields**: `transferId`, `totalBytes`, `resumedFrom`, `verified`, `usedFastPath` (ready for future `remote_copy_status`)
246
+ - **Verify modes**: `verify=none|size|size+sha256` (default `size`) for Hop1 transfers
247
+ - Small files still use `fastGet`/`fastPut`; large files or `resume`/progress use streamed copy
248
+
249
+ ### Changed
250
+
251
+ - `executeRemoteCopy` now takes an options object (breaking for direct JS callers; MCP schema remains backward compatible via optional fields)
252
+
253
+ ## [2.0.0] - 2026-07-06
254
+
255
+ ### Removed
256
+
257
+ - **`remote_tail_file` tool** — use `remote_read_file` with `mode=tail` instead (breaking change)
258
+
259
+ ### Changed
260
+
261
+ - Tool count reduced from 9 to 8; all docs/prompts/instructions updated
262
+
263
+ ## [1.4.0] - 2026-07-06
264
+
265
+ ### Added
266
+
267
+ - `remote_read_file` unified file reading: `mode=head|tail`, shared `lines` parameter
268
+ - Tail reads now include `totalLines` / `totalBytes` metadata (same as head)
269
+ - `hop2PathUnion()` shared schema builder for bastion/pod/host targets (P0 refactor)
270
+ - Tests for `resolveReadOptions`, `buildRemoteReadCommand`, deprecated tail alias
271
+
272
+ ### Changed
273
+
274
+ - **`remote_tail_file` deprecated** — delegates to `remote_read_file` with `mode=tail`; kept for backward compatibility
275
+ - `maxLines` on `remote_read_file` remains as deprecated alias for `lines` when `mode=head`
276
+
277
+ ## [1.2.0] - 2026-07-06
278
+
279
+ ### Added
280
+
281
+ - **`remote_copy_file` `local` endpoint**: SFTP upload/download between MCP server machine and bastion
282
+ - Chained routes: `local→pod`, `local→host`, `pod→local`, `host→local` (via bastion staging)
283
+
284
+ ## [1.1.0] - 2026-07-06
285
+
286
+ ### Added
287
+
288
+ - **`remote_copy_file` tool**: copy files/directories between bastion, pod, and host; returns `{ success, bytes, message }` without file content
289
+ - **`remote_read_file` metadata**: response includes `totalLines`, `totalBytes`, `truncatedByLines`, `truncatedByChars` for clearer truncation diagnosis
290
+ - Unit tests for copy command builders and read-file metadata parsing
291
+
292
+ ### Changed
293
+
294
+ - `remote_read_file` tool description documents default `maxLines=500` and `MAX_OUTPUT_CHARS` (65536) limits
295
+ - Server instructions mention `remote_copy_file` and updated truncation guidance
296
+
297
+ ## [1.0.1] - 2026-07-03
298
+
299
+ ### Fixed
300
+
301
+ - Bastion SSH auth: pass all configured methods (key, agent, password) to ssh2 instead of picking only the first — fixes empty SSH agent blocking `BASTION_PASSWORD` in IDE-launched MCP clients
302
+
303
+ ### Added
304
+
305
+ - Unit tests: `truncateOutput`, `buildReadFileCommand` path escaping, `resolveSshAuthOptions` auth priority (injectable deps)
306
+ - ESLint + Prettier, `CONTRIBUTING.md`
307
+ - Expanded `SECURITY.md` (two-hop model, sshpass visibility, accept-new, ALLOWED_NAMESPACES scope)
308
+
309
+ ### Changed
310
+
311
+ - Removed `parseRemoteTarget`; `remote_*` targets unified via `remote-schemas.ts` (`remoteInputToTarget` + `RemoteTarget` type)
312
+ - `resolveSshAuthOptions` accepts optional deps for testability
313
+
314
+ ## [1.0.0] - 2026-07-02
315
+
316
+ ### Added
317
+
318
+ - MCP server with 8 tools: `bastion_exec`, `k8s_list_pods`, `k8s_exec`, `k8s_logs`, `ssh_exec`, `remote_read_file`, `remote_tail_file`, `remote_ps`
319
+ - Two-hop architecture: MCP client → SSH → bastion → kubectl / SSH / shell
320
+ - Flexible bastion auth: SSH agent, default keys, `BASTION_PRIVATE_KEY_PATH`, or `BASTION_PASSWORD`
321
+ - Remote host password support via `REMOTE_SSH_PASSWORD` / tool `password` (sshpass on bastion)
322
+ - MCP prompts: `tool-selection`, `remote-debug-workflow`
323
+ - Agent documentation (`docs/AGENT_GUIDE.md`) and configuration guide
324
+ - Manual integration scripts under `scripts/manual/` (env-driven, not published to npm)
325
+ - GitHub Actions CI (build + unit tests)
326
+
327
+ ### Security
328
+
329
+ - Removed hardcoded credentials from manual scripts; use `scripts/manual/.env.local` instead
330
+ - Added `SECURITY.md` with credential and sshpass guidance
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 host-execution-mcp contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # host-execution-mcp
2
+
3
+ Generic [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server for Access → HostExecutionContext → Runtime (shell / docker / kubectl via SSH jump or Paradi gateway).
4
+
5
+ **Any MCP client** can connect (Cursor, Claude Desktop, VS Code, custom SDK) using stdio transport.
6
+
7
+ ## What it does
8
+
9
+ ```
10
+ MCP Client → host-execution-mcp (local) → SSH → ACCESS landing Context → kubectl / docker / shell / optional derive → pods & hosts
11
+ ```
12
+
13
+ Composable tools:
14
+
15
+ | Tool | Purpose |
16
+ |------|---------|
17
+ | `access_check` | Short ACCESS SSH probe (`reachable` / `latencyMs`; not kubectl) |
18
+ | `shell_exec` | Shell on landing Context (optional `contextKey`) |
19
+ | `context_derive` | Derive child Context → `contextKey` (`ACCESS_KIND=ssh` only) |
20
+ | `context_probe` | Probe docker/kubectl capabilities (unknown ≠ true) |
21
+ | `docker_ps` / `docker_exec` / `docker_logs` | Docker Runtime on landing or derived Context (`contextKey`) |
22
+ | `k8s_list_pods` | List pods (`kubectl get pods`) |
23
+ | `k8s_exec` | Run command in pod container |
24
+ | `k8s_logs` | Pod stdout/stderr (`kubectl logs`) |
25
+ | `ssh_exec` | Derive + shell on remote host (`ACCESS_KIND=ssh`) |
26
+ | `remote_read_file` | Read file head/tail (`mode=head\|tail`) on landing/pod/context |
27
+ | `remote_copy_file` | Sync copy between local/landing/pod/context (SFTP for local↔landing; `skipIfComplete` / `totalTimeoutMs` / progress) |
28
+ | `remote_copy_start` | Async copy job — returns `transferId` immediately (prefer for >256MB or ETA>2min) |
29
+ | `remote_copy_status` | Poll async copy job progress |
30
+ | `remote_copy_cancel` | Cancel async copy job |
31
+ | `remote_ps` | List processes on landing/pod/context |
32
+
33
+ Async job state: `~/.agent-compose/remote-copy/jobs/<transferId>.json` — see [docs/REMOTE_COPY_JOB_API.md](docs/REMOTE_COPY_JOB_API.md).
34
+
35
+ No business logic, no hardcoded namespaces or paths.
36
+
37
+ ## Quick start
38
+
39
+ ### Option A — npx(推荐,无需 clone/build)
40
+
41
+ Requires Node.js >= 18 only.
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "host-execution": {
47
+ "command": "npx",
48
+ "args": ["-y", "host-execution-mcp@latest"],
49
+ "env": {
50
+ "ACCESS_HOST": "10.0.0.1",
51
+ "ACCESS_USER": "your-user"
52
+ }
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ Pin a version for stability: `"args": ["-y", "host-execution-mcp@6.0.0"]`
59
+
60
+ ### Option B — 本地开发
61
+
62
+ ```bash
63
+ cd packages/mcp/host-execution
64
+ npm install
65
+ npm run build
66
+ ```
67
+
68
+ MCP config uses local path:
69
+
70
+ ```json
71
+ "command": "node",
72
+ "args": ["<ABS_PATH>/packages/mcp/host-execution/dist/index.js"]
73
+ ```
74
+
75
+ ### Minimal env
76
+
77
+ Only `ACCESS_HOST` and `ACCESS_USER` are required. Configure any SSH auth you use (key path, default `~/.ssh` keys, SSH agent, and/or `ACCESS_PASSWORD`); the client tries each until one works. If your IDE exposes an empty SSH agent, set `ACCESS_PASSWORD` and it will be used after agent/key attempts fail. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md).
78
+
79
+ ## Publishing (maintainers)
80
+
81
+ ```bash
82
+ npm run build && npm test
83
+ npm publish # or --registry for private npm
84
+ ```
85
+
86
+ ## Documentation
87
+
88
+ - [使用教程(中文)](docs/USER_GUIDE.zh-CN.md) — Node.js 安装、MCP 配置、Agent 实用话术
89
+ - [Agent usage guide](docs/AGENT_GUIDE.md) — for LLM agents after MCP handshake
90
+ - [Configuration](docs/CONFIGURATION.md) — env vars, auth, client setup
91
+ - [SECURITY.md](SECURITY.md) — trust boundaries, credentials, sshpass notes
92
+ - [CHANGELOG.md](CHANGELOG.md)
package/SECURITY.md ADDED
@@ -0,0 +1,72 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ | Version | Supported |
6
+ |---------|-----------|
7
+ | 6.0.x | Yes |
8
+ | 5.0.x | Best effort |
9
+ | 4.0.x | Best effort |
10
+ | < 4.0 | Best effort |
11
+
12
+ ## Reporting a vulnerability
13
+
14
+ If you discover a security issue, please report it privately to the maintainers (do not open a public issue with exploit details).
15
+
16
+ ## Architecture and trust boundaries
17
+
18
+ Operations follow **Access → HostExecutionContext → Runtime** (`access_check`, `shell_exec`, `context_*`, `k8s_*`, `docker_*`, `remote_*`):
19
+
20
+ ```
21
+ MCP client → SSH → ACCESS_HOST (landing Context)
22
+ → kubectl / docker / shell / optional derive(ssh) → Pod or host
23
+
24
+ ACCESS_KIND=paradi-gateway: SSH username encodes TARGET; landing Context = TARGET (not the gateway OS).
25
+ ```
26
+
27
+ There is **no direct path** from the MCP client machine to cluster pods or worker nodes. Treat compromise of the ACCESS landing (or Paradi gateway) as compromise of every target it can reach.
28
+
29
+ ## Credential storage
30
+
31
+ | Credential | Typical location | Notes |
32
+ |------------|------------------|-------|
33
+ | ACCESS SSH key / agent | Local `~/.ssh`, `SSH_AUTH_SOCK`, or `ACCESS_PRIVATE_KEY_PATH` | Preferred over password; agent and password may both be configured — ssh2 tries each until one succeeds |
34
+ | ACCESS password | MCP client env (`ACCESS_PASSWORD`) or `.env` | Dev only; never commit |
35
+ | Target host password | MCP env (`TARGET_PASSWORD`) or per-tool `password` | Uses `sshpass` on ACCESS for derive (`ssh` kind) |
36
+ | MCP client config | `.cursor/mcp.json`, Claude Desktop config, etc. | Often gitignored; treat as secret |
37
+
38
+ **Never commit** `.env`, `scripts/manual/.env.local`, MCP configs with passwords, or private keys. Rotate credentials if they were ever committed or shared in chat logs.
39
+
40
+ ## sshpass and process visibility
41
+
42
+ When `ssh_exec` / `context_derive` use a password (or `TARGET_PASSWORD`), the ACCESS landing runs:
43
+
44
+ ```bash
45
+ sshpass -p '<password>' ssh ...
46
+ ```
47
+
48
+ The password may appear in **`ps` / process listings on the landing** while the command runs. **Prefer SSH keys** for ACCESS→host derive. If passwords are unavoidable, restrict landing shell access and audit who can run MCP.
49
+
50
+ ## StrictHostKeyChecking=accept-new
51
+
52
+ Remote host SSH uses `-o StrictHostKeyChecking=accept-new`. On first connect, unknown host keys are accepted automatically. This is intended for **controlled internal networks** where MITM risk is managed by network policy. For internet-facing hosts, pre-populate `known_hosts` on the bastion and consider key-based auth only.
53
+
54
+ ## ALLOWED_NAMESPACES scope
55
+
56
+ `ALLOWED_NAMESPACES` restricts **kubectl tools only** (`k8s_list_pods`, `k8s_exec`, `k8s_logs`, and `remote_*` with `target=pod`). It does **not** restrict:
57
+
58
+ - `shell_exec` — arbitrary shell on the ACCESS landing Context, including `kubectl` against any namespace
59
+ - `ssh_exec` — SSH derive from ACCESS to any reachable host
60
+ - `remote_*` with `target=landing` or materialized `target=context`
61
+
62
+ For defense in depth, combine namespace whitelist with bastion RBAC, kubeconfig permissions, and limiting who can run the MCP server.
63
+
64
+ ## Operational risks
65
+
66
+ - This server executes arbitrary shell commands on the bastion, in pods, and on remote hosts. There is no built-in command allowlist in v1.x.
67
+ - Treat MCP access as equivalent to shell access on the bastion plus kubectl access in allowed namespaces.
68
+ - Output is truncated at `MAX_OUTPUT_CHARS` (default 65536) to limit accidental large dumps.
69
+
70
+ ## Dependencies
71
+
72
+ Run `npm audit` periodically and keep `@modelcontextprotocol/sdk`, `ssh2`, and `zod` updated.
@@ -0,0 +1,38 @@
1
+ import type { ExecOutput } from "../ssh/access-client.js";
2
+ import type { SessionPool } from "./session-pool.js";
3
+ import type { HostExecutionContext } from "./types.js";
4
+ /** Docker Runtime — only consumes Context + SessionPool (Invariant B). */
5
+ export declare function buildDockerPsCommand(options: {
6
+ all?: boolean;
7
+ quiet?: boolean;
8
+ filter?: string;
9
+ }): string;
10
+ export declare function buildDockerExecCommand(container: string, command: string, options?: {
11
+ interactive?: boolean;
12
+ workdir?: string;
13
+ }): string;
14
+ export declare function buildDockerLogsCommand(container: string, options?: {
15
+ tail?: number;
16
+ since?: string;
17
+ timestamps?: boolean;
18
+ }): string;
19
+ export declare function ensureDocker(pool: SessionPool, ctx: HostExecutionContext, timeoutMs?: number): Promise<void>;
20
+ export declare function dockerPs(pool: SessionPool, ctx: HostExecutionContext, options?: {
21
+ all?: boolean;
22
+ quiet?: boolean;
23
+ filter?: string;
24
+ timeoutMs?: number;
25
+ }): Promise<ExecOutput>;
26
+ export declare function dockerExec(pool: SessionPool, ctx: HostExecutionContext, args: {
27
+ container: string;
28
+ command: string;
29
+ workdir?: string;
30
+ timeoutMs?: number;
31
+ }): Promise<ExecOutput>;
32
+ export declare function dockerLogs(pool: SessionPool, ctx: HostExecutionContext, args: {
33
+ container: string;
34
+ tail?: number;
35
+ since?: string;
36
+ timestamps?: boolean;
37
+ timeoutMs?: number;
38
+ }): Promise<ExecOutput>;
@@ -0,0 +1,61 @@
1
+ import { bashSingleQuote } from "../utils/shell-quote.js";
2
+ import { assertCapability, probeCapabilities } from "./probe.js";
3
+ /** Docker Runtime — only consumes Context + SessionPool (Invariant B). */
4
+ export function buildDockerPsCommand(options) {
5
+ const parts = ["docker", "ps"];
6
+ if (options.all) {
7
+ parts.push("-a");
8
+ }
9
+ if (options.quiet) {
10
+ parts.push("-q");
11
+ }
12
+ if (options.filter?.trim()) {
13
+ parts.push("--filter", bashSingleQuote(options.filter.trim()));
14
+ }
15
+ // Prefer stable columns for agents (skip format when quiet)
16
+ if (!options.quiet) {
17
+ parts.push("--format", bashSingleQuote("table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}"));
18
+ }
19
+ return parts.join(" ");
20
+ }
21
+ export function buildDockerExecCommand(container, command, options = {}) {
22
+ const parts = ["docker", "exec"];
23
+ if (options.workdir?.trim()) {
24
+ parts.push("-w", bashSingleQuote(options.workdir.trim()));
25
+ }
26
+ parts.push(bashSingleQuote(container.trim()));
27
+ // Run via bash -lc so pipelines/redirection work like other Runtimes
28
+ parts.push("bash", "-lc", bashSingleQuote(command));
29
+ return parts.join(" ");
30
+ }
31
+ export function buildDockerLogsCommand(container, options = {}) {
32
+ const parts = ["docker", "logs"];
33
+ if (options.timestamps) {
34
+ parts.push("-t");
35
+ }
36
+ if (options.since?.trim()) {
37
+ parts.push("--since", bashSingleQuote(options.since.trim()));
38
+ }
39
+ parts.push("--tail", String(options.tail ?? 200));
40
+ parts.push(bashSingleQuote(container.trim()));
41
+ return parts.join(" ");
42
+ }
43
+ export async function ensureDocker(pool, ctx, timeoutMs) {
44
+ if (ctx.capabilities.docker === "unknown") {
45
+ await probeCapabilities(pool, ctx, timeoutMs);
46
+ }
47
+ assertCapability(ctx, "docker");
48
+ }
49
+ export async function dockerPs(pool, ctx, options = {}) {
50
+ await ensureDocker(pool, ctx, options.timeoutMs);
51
+ return pool.exec(ctx, buildDockerPsCommand(options), options.timeoutMs);
52
+ }
53
+ export async function dockerExec(pool, ctx, args) {
54
+ await ensureDocker(pool, ctx, args.timeoutMs);
55
+ return pool.exec(ctx, buildDockerExecCommand(args.container, args.command, { workdir: args.workdir }), args.timeoutMs);
56
+ }
57
+ export async function dockerLogs(pool, ctx, args) {
58
+ await ensureDocker(pool, ctx, args.timeoutMs);
59
+ return pool.exec(ctx, buildDockerLogsCommand(args.container, args), args.timeoutMs);
60
+ }
61
+ //# sourceMappingURL=docker-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-runtime.js","sourceRoot":"","sources":["../../src/access/docker-runtime.ts"],"names":[],"mappings":"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"}