ftp-deploy-mcp 0.0.0-stage → 0.3.1

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 (89) hide show
  1. package/CHANGELOG.fr.md +468 -0
  2. package/CHANGELOG.md +415 -0
  3. package/CONTRIBUTING.fr.md +120 -0
  4. package/CONTRIBUTING.md +111 -0
  5. package/LICENSE +21 -0
  6. package/LICENSE.fr.md +26 -0
  7. package/README.fr.md +518 -0
  8. package/README.md +501 -2
  9. package/SECURITY.fr.md +99 -0
  10. package/SECURITY.md +94 -0
  11. package/docs/AGENT-PERFORMANCE.fr.md +144 -0
  12. package/docs/AGENT-PERFORMANCE.md +133 -0
  13. package/docs/ERROR-CONTRACT.fr.md +141 -0
  14. package/docs/ERROR-CONTRACT.md +137 -0
  15. package/docs/LANGUAGES.fr.md +68 -0
  16. package/docs/LANGUAGES.md +62 -0
  17. package/docs/RELEASE.fr.md +462 -0
  18. package/docs/RELEASE.md +435 -0
  19. package/docs/RESOURCE-BOUNDS.fr.md +144 -0
  20. package/docs/RESOURCE-BOUNDS.md +132 -0
  21. package/docs/SCRIPTED-EVALUATIONS.fr.md +190 -0
  22. package/docs/SCRIPTED-EVALUATIONS.md +181 -0
  23. package/docs/SECURITY-MODEL.fr.md +262 -0
  24. package/docs/SECURITY-MODEL.md +236 -0
  25. package/docs/STATE-STORAGE.fr.md +236 -0
  26. package/docs/STATE-STORAGE.md +231 -0
  27. package/docs/TRANSFERS.fr.md +140 -0
  28. package/docs/TRANSFERS.md +123 -0
  29. package/docs/WORKFLOW-MODEL.fr.md +63 -0
  30. package/docs/WORKFLOW-MODEL.md +62 -0
  31. package/evaluations/README.fr.md +95 -0
  32. package/evaluations/README.md +92 -0
  33. package/evaluations/fixture/README.txt +3 -0
  34. package/evaluations/fixture/catalog/alpha.txt +4 -0
  35. package/evaluations/fixture/catalog/bravo.txt +4 -0
  36. package/evaluations/fixture/catalog/charlie.txt +4 -0
  37. package/evaluations/fixture/catalog/delta.txt +4 -0
  38. package/evaluations/fixture/catalog/echo.txt +4 -0
  39. package/evaluations/fixture/catalog/foxtrot.txt +4 -0
  40. package/evaluations/fixture/reports/checks.txt +4 -0
  41. package/evaluations/fixture/reports/release.txt +3 -0
  42. package/evaluations/read-only.fr.xml +42 -0
  43. package/evaluations/read-only.xml +42 -0
  44. package/ftp-servers.example.json +27 -0
  45. package/install.cmd +29 -0
  46. package/install.sh +18 -0
  47. package/package.json +88 -4
  48. package/server.json +35 -0
  49. package/src/adapters/ftp.js +392 -0
  50. package/src/adapters/sftp.js +647 -0
  51. package/src/admission.js +15 -0
  52. package/src/atomic-write.js +131 -0
  53. package/src/clients.js +212 -0
  54. package/src/config.js +629 -0
  55. package/src/durable/confirmation.js +88 -0
  56. package/src/durable/engine.js +696 -0
  57. package/src/durable/plan.js +238 -0
  58. package/src/durable/schemas.js +206 -0
  59. package/src/durable/state.js +317 -0
  60. package/src/durable/status.js +175 -0
  61. package/src/durable-tools.js +94 -0
  62. package/src/errors.js +88 -0
  63. package/src/filezilla.js +260 -0
  64. package/src/i18n.js +58 -0
  65. package/src/index.js +175 -0
  66. package/src/local-path.js +130 -0
  67. package/src/locales/en.js +152 -0
  68. package/src/locales/errors.en.js +22 -0
  69. package/src/locales/errors.fr.js +22 -0
  70. package/src/locales/fr.js +152 -0
  71. package/src/locales/runtime.en.js +259 -0
  72. package/src/locales/runtime.fr.js +259 -0
  73. package/src/operations.js +276 -0
  74. package/src/redact.js +105 -0
  75. package/src/remote-path.js +101 -0
  76. package/src/scanner.js +103 -0
  77. package/src/setup.js +888 -0
  78. package/src/state/codec.mjs +103 -0
  79. package/src/state/index.mjs +1 -0
  80. package/src/state/io.mjs +173 -0
  81. package/src/state/records.mjs +60 -0
  82. package/src/state/store.mjs +360 -0
  83. package/src/state/writer.mjs +250 -0
  84. package/src/tool-registry.js +464 -0
  85. package/src/tools.js +1226 -0
  86. package/src/transfers.js +425 -0
  87. package/src/workflow/budget.mjs +83 -0
  88. package/src/workflow/events.mjs +199 -0
  89. package/src/workflow/model.mjs +179 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,415 @@
1
+ # Changelog
2
+
3
+ **English** | [Français](./CHANGELOG.fr.md)
4
+
5
+ All notable changes to this project will be documented in this file.
6
+
7
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
8
+ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
+
10
+ ## [0.3.1] - 2026-10-17
11
+
12
+ This patch release is limited to release qualification and a supply-chain lock
13
+ refresh. It preserves the immutable v0.3.0 tag and GitHub source release. npm
14
+ and Official MCP Registry publication remain pending.
15
+
16
+ ### Fixed
17
+
18
+ - Release qualification now locks the optional transitive `nan` dependency to
19
+ 2.29.0, including its reviewed registry URL and integrity, matching the
20
+ version resolved by a fresh installation. No runtime behavior or public API
21
+ changes.
22
+
23
+ ### Changed
24
+
25
+ - Package, lockfile, MCP server and bilingual release metadata now consistently
26
+ target v0.3.1. The complete v0.3.0 history remains below.
27
+
28
+ ## [0.3.0] - 2026-10-17
29
+
30
+ This minor release uses the separate GitHub source release channel; the published v0.2.0 tag and
31
+ release remain unchanged. npm and Official MCP Registry publication remain
32
+ pending. Install from source; no npm, `npx` or registry availability is claimed.
33
+
34
+ ### Added
35
+
36
+ - Optional durable deployment with `stateDir`: `ftp_plan_deploy`,
37
+ `ftp_apply_deploy`, `ftp_deploy_status`, and `ftp_rollback_deploy`. The
38
+ workflow uses official MCP `input_required` confirmation, sealed journals,
39
+ bounded verified backups, explicit resume, and conservative reconciliation.
40
+ Its guarantee is `observable_state_reconciled`, not multi-file atomicity or
41
+ exactly-once execution.
42
+ - Privacy-safe state format v2 stores a domain-salted local-root hash and strict
43
+ relative source paths, never the absolute local root. Apply revalidates local
44
+ identity before connection; rollback remains usable after local-root drift.
45
+ State v1 requires offline reinitialization.
46
+
47
+ ### Fixed
48
+
49
+ - Internal preparation is single-use, capped at 10 seconds without extending
50
+ the overall deadline, and retains admission until actual work and cleanup
51
+ settle. Prepared contexts are released once, including late completion after
52
+ cancellation. A cancelled or expired preparation never invokes the handler;
53
+ the preparation callback itself may act. No public MCP tool is added.
54
+ - Release qualification compares the complete expected package manifest,
55
+ authoritative source lock and all shipped source bytes. Unapproved dependency
56
+ modifiers and installation hooks are refused before consumer installation.
57
+ - Archive inspection reads and hashes compressed bytes once, performs strict
58
+ gzip decoding with a 32 MiB aggregate output cap, rejects nonzero data after
59
+ the consumed gzip boundary, and supplies the same decoded TAR to every tar
60
+ invocation. Terminal NUL padding is allowed; gzip/TAR member boundaries
61
+ cannot hide extra files from validation.
62
+ - npm and MCP workflows separate qualification without OIDC or secrets from
63
+ publication in a fresh checkout pinned to `github.sha`. npm transfers the
64
+ exact uploaded artifact ID and original inventory/integrity values; the
65
+ publication job validates those bindings before creating its local proof.
66
+ Dependency installation, tests, smoke checks and `--runtime` execute only
67
+ outside privileged publication jobs.
68
+ - Release metadata parsing accepts the supported npm pack JSON array/object
69
+ forms consistently in CI. Changelog dates must be valid calendar dates.
70
+
71
+ ### Changed
72
+
73
+ - The MCP runtime now uses the stable modular TypeScript SDK v2.3.1:
74
+ `@modelcontextprotocol/server` is the exact production pin, while the exact
75
+ client and raw-wire core packages remain development-only. The stdio entry
76
+ uses the SDK's `serveStdio` factory, negotiates revision `2026-07-28`, and
77
+ retains the legacy client path, including revision `2024-11-05`.
78
+ - The five direct production dependencies are pinned exactly; the sole
79
+ `package-lock.json` retains 35 reviewed non-root records (22 production and
80
+ 13 development). No shrinkwrap is shipped.
81
+ - The builder produces a 90-file source installation archive with its lockfile,
82
+ distinct from the 89-file npm package without a lockfile. Proofs explicitly
83
+ bind the distribution, sources and integrity.
84
+ - Source installation uses `npm ci --omit=dev --ignore-scripts`; contributor
85
+ setup uses `npm ci --ignore-scripts`. Archive bytes may vary across systems.
86
+ The actual installed graph, including transitive dependencies and peers, is
87
+ compared with the original authority before publication.
88
+ - npm remains unqualified after ten observed changes in a real installation.
89
+ MCP qualification checks the actual registry download, then the privileged
90
+ job requires its original integrity before authentication.
91
+
92
+ - English/French installation and release documentation distinguishes GitHub
93
+ source releases from npm and Official MCP Registry availability.
94
+
95
+ Scripted tests are not an LLM benchmark, and this minor release makes no
96
+ enterprise-certification claim.
97
+
98
+ ## [0.2.0] - 2026-09-09
99
+
100
+ The GitHub tag and release are cut from this commit. Publication to npm and to
101
+ the MCP registry is still pending, so `npx -y ftp-deploy-mcp` and registry-based
102
+ installation do not work yet; install from source. Package and server metadata
103
+ are aligned at 0.2.0. All changes below are relative to 0.1.0, the only version
104
+ ever released.
105
+
106
+ ### Added
107
+
108
+ - Bilingual command line. `--lang en` and `--lang fr` (also `--lang=fr`) select
109
+ the language of general and subcommand help, `setup` prompts and choices,
110
+ connection-test labels, `doctor` diagnostics, `import-filezilla` warnings and
111
+ server startup messages. The new `FTP_MCP_LANG` environment variable sets the
112
+ same default, and the option takes precedence over it, including over an
113
+ invalid environment value. The option is read before the subcommand parser and
114
+ may appear before or after the subcommand. Only `en` and `fr` are accepted; an
115
+ invalid or incomplete option exits unsuccessfully before `setup` or
116
+ `import-filezilla` writes anything. OS locale variables such as `LANG` are
117
+ never consulted. See [languages](./docs/LANGUAGES.md).
118
+ - Localized MCP contracts. Tool titles, descriptions, input field descriptions,
119
+ business success text and error messages follow the selected language. The ten
120
+ tool names, structured result field names and public error codes are stable and
121
+ are not translated, and no tool argument selects a language.
122
+ - `setup` writes `FTP_MCP_LANG` into the MCP client entries it generates,
123
+ including `en`, so a configured server keeps its setup language after a
124
+ restart. The paste-ready Trae snippet carries the same setting.
125
+ - Structured error envelope on every tool error. Alongside the `isError` text,
126
+ results now carry `structuredContent.error` with `schema_version`, `code`,
127
+ `message`, `retryable`, a per-call `request_id` UUID, `next_action` and
128
+ `effects`. Upload, download and deployment errors can add a strict `partial`
129
+ block (`completed_files`, `completed_bytes`, `failed_files`, optional
130
+ `total_files`, and `final`). `request_id` is not a durable operation
131
+ identifier; there is no journal, rollback or recovery command. This release
132
+ returns `retryable: false` for every error and never retries an uncertain
133
+ mutation. See the [error contract](./docs/ERROR-CONTRACT.md).
134
+ - Six per-server configuration keys, each with an enforced default and maximum.
135
+ Tool arguments cannot override them, and values must be positive safe integers:
136
+ `operationTimeoutMs` (default 120000 ms, accepted range 100–3600000),
137
+ `maxTransferBytes` (default 268435456, maximum 1099511627776),
138
+ `maxDeployFiles` (default 10000, maximum 100000),
139
+ `maxDeployBytes` (default 1073741824, maximum 1099511627776),
140
+ `maxScanEntries` (default 100000, maximum 1000000) and
141
+ `maxScanDepth` (default 64, maximum 256). These defaults apply to servers that
142
+ do not set them, so a deployment that succeeded on 0.1.0 can now be refused
143
+ with `TRANSFER_LIMIT` or `SCAN_LIMIT` before any connection is opened.
144
+ **BREAKING — `operationTimeoutMs` also introduces a deadline 0.1.0 never
145
+ had:** every tool call now aborts with `TIMEOUT` after 120000 ms by default,
146
+ and unlike the other limits it fires mid-flight rather than refusing up
147
+ front, so a large or slow `ftp_deploy` that ran to completion on 0.1.0 can
148
+ stop part-way with files already promoted and no rollback. Raise
149
+ `operationTimeoutMs` (up to 3600000) for slow links. See
150
+ [verified transfers](./docs/TRANSFERS.md) and
151
+ [resource limits](./docs/RESOURCE-BOUNDS.md).
152
+ - Process-wide admission control. One Node isolate admits at most 64 concurrent
153
+ tool calls; the 65th returns the new `CAPACITY_LIMIT` with `effects:none`,
154
+ `retryable:false` and `next_action:retry`. This is a concurrency cap, not a
155
+ lifetime quota: a slot is released when its worker and cleanup actually settle,
156
+ including after cancellation or timeout, and there is no queue or automatic
157
+ retry. Admission happens after schema validation and before preparation.
158
+ - MCP cancellation and per-call deadlines. A peer `notifications/cancelled`
159
+ aborts the operation and the SDK suppresses its result, so clients must not
160
+ expect a delivered `CANCELLED` envelope; the separate internal deadline
161
+ returns `TIMEOUT`. Cancellation and expiry stop later stages and close the
162
+ transport, but do not undo effects already accepted by the server. A public
163
+ transport decorator corrects the installed SDK's cancellation gap for the
164
+ numeric correlation ID `0` and the empty string `""`; other IDs, including the
165
+ string `"0"`, keep native SDK behaviour.
166
+ - Serialized mutations. Writing tools take a FIFO lock on the remote endpoint
167
+ (protocol, host, port, user) and `ftp_download` takes one on the canonical
168
+ local destination, so two calls cannot interleave writes on the same target.
169
+ Locks are held until the underlying worker actually settles, not until the
170
+ response is sent, and they coordinate only within one Node.js process.
171
+ - Per-server `localRoot` boundary for `ftp_upload`, `ftp_deploy`, and
172
+ `ftp_download`. The root must be absolute (with `~` expansion supported);
173
+ traversal and local symlink/junction escapes are refused.
174
+ - SFTP host-key pinning through `hostKeySha256`, accepting one SHA-256
175
+ fingerprint or a non-empty array for controlled key rotation.
176
+ - Explicit `allowUnknownHostKey` override for operators who temporarily accept
177
+ an unverified SFTP server identity.
178
+ - Explicit `allowUnsafeRemoteRoot` override for FTP/FTPS operators who accept
179
+ the unresolved symlink risk of a client-side sub-root.
180
+ - MCP output schemas and successful structured responses for every tool except
181
+ the intentionally text-only `ftp_read`, plus annotations for all ten tools.
182
+ - Bilingual documentation set, each page in English and French:
183
+ [security model](./docs/SECURITY-MODEL.md),
184
+ [release process](./docs/RELEASE.md),
185
+ [error contract](./docs/ERROR-CONTRACT.md),
186
+ [languages](./docs/LANGUAGES.md),
187
+ [verified transfers](./docs/TRANSFERS.md),
188
+ [resource limits](./docs/RESOURCE-BOUNDS.md),
189
+ [state storage](./docs/STATE-STORAGE.md),
190
+ [workflow model](./docs/WORKFLOW-MODEL.md) and
191
+ [scripted MCP conformance](./docs/SCRIPTED-EVALUATIONS.md). `CONTRIBUTING`,
192
+ `SECURITY`, `LICENSE` and this changelog gain French counterparts, joining the
193
+ `README` pair that already shipped in 0.1.0; both `README` languages were
194
+ rewritten for this release.
195
+ - A bilingual interactive HTML guide under `site/`, generated from
196
+ `site/project-data.json` and `site/i18n.json` and verified in CI by
197
+ `scripts/build-guide.mjs --check`. It lives in the repository only and is
198
+ excluded from the npm package.
199
+ - A reproducible, external read-only agent evaluation fixture and 10-question
200
+ MCP evaluation set, now in English (`evaluations/read-only.xml`) and French
201
+ (`evaluations/read-only.fr.xml`), plus a separate scripted conformance runner
202
+ of 43 scenarios executed in both languages against the real MCP SDK client and
203
+ in-memory transport (`npm run eval:scripted`). The runner is repository
204
+ tooling, is not an LLM benchmark, and is excluded from the npm package. See
205
+ [scripted MCP conformance](./docs/SCRIPTED-EVALUATIONS.md).
206
+ - Internal, inert libraries `src/state/` and `src/workflow/`. They ship inside
207
+ the package because it publishes `src`, but they expose no MCP tool, read no
208
+ configuration, create no state directory and are not imported by the running
209
+ server. They deliver no deployment workflow, rollback or recovery in this
210
+ release; both [state storage](./docs/STATE-STORAGE.md) and the
211
+ [workflow model](./docs/WORKFLOW-MODEL.md) state this explicitly.
212
+ - Contributor-facing npm scripts: `test:transports`, `test:contract`,
213
+ `test:bounds`, `test:state`, `test:workflow`, `test:eval-runner` and
214
+ `eval:scripted`. CI additionally runs `scripts/check-docs.mjs` for bilingual
215
+ documentation and `test/release-gates.js`.
216
+ - `server.json` MCP registry metadata, declaring the `FTP_MCP_CONFIG` and
217
+ `FTP_MCP_LANG` environment variables and a stdio transport.
218
+ - A repository `Dockerfile` and `.dockerignore` building a `node:24-alpine`
219
+ image whose entrypoint is the stdio server; mount a configuration file and
220
+ point `FTP_MCP_CONFIG` at it. No image is published, and neither file is
221
+ included in the npm package.
222
+
223
+ ### Changed
224
+
225
+ - **BREAKING — Node.js 18, 19, 20 and 21 are no longer supported.**
226
+ `engines.node` moves from `>=18` to `>=22`. CI qualifies Node 22 and 24 on
227
+ Linux, Windows and macOS.
228
+ - **BREAKING — an explicit configuration selector now fails closed.** When
229
+ `--config <path>` or `FTP_MCP_CONFIG` is present, that single path is
230
+ authoritative: it must exist, be a regular file and load successfully, and no
231
+ other candidate is attempted. `--config` takes priority over
232
+ `FTP_MCP_CONFIG`, and an empty explicit value is rejected. Only when neither
233
+ is set does discovery still try `./ftp-servers.json` then
234
+ `~/.ftp-mcp/servers.json`. In 0.1.0 both selectors were merely the first
235
+ entries of a fallback list, so a missing or unreadable explicit path silently
236
+ fell through to another file. The server still starts; every tool that needs
237
+ the configuration now fails with `CONFIG_INVALID` instead of operating on an
238
+ unintended configuration, and `ftp_list_servers` returns `status: "invalid"`
239
+ with the loader diagnostic.
240
+ - One rejected server entry no longer disables the whole configuration. Entries
241
+ are validated individually: valid servers stay usable, and each invalid one is
242
+ reported by name with its reason through `ftp_list_servers` (`status`,
243
+ `valid_count`, `invalid_count` and a bounded `errors` list). Naming a rejected
244
+ entry returns that entry's `CONFIG_INVALID`, an unknown name
245
+ returns `SERVER_UNKNOWN`, and omitting `server` with several valid candidates
246
+ and no `defaultServer` returns `SERVER_REQUIRED`. In 0.1.0 the first invalid
247
+ entry made the entire file unusable and every tool call failed. Envelope
248
+ problems — no `servers` object, an empty one, an unknown `defaultServer`, an
249
+ unset `${ENV:VAR}` — still reject the whole file, as does a file in which
250
+ every entry is invalid.
251
+ - **BREAKING — `localRoot` is now required** by `ftp_upload`, `ftp_deploy` and
252
+ `ftp_download`, **and relative tool paths now resolve against it instead of
253
+ the server process's working directory.** A server entry without an absolute
254
+ `localRoot` refuses those three tools with `CONFIG_INVALID`. In 0.1.0
255
+ `local_path` and `local_dir` resolved against `process.cwd()`, so an unchanged
256
+ call such as `ftp_deploy {"local_dir":"dist"}` now reads `<localRoot>/dist`
257
+ instead of `<cwd>/dist` — it can silently select a different directory, or
258
+ fail with `NOT_FOUND` or `PATH_REJECTED` when the old target sits outside the
259
+ root. Absolute paths keep their meaning and must still resolve inside
260
+ `localRoot`. `ftp_list_servers` reports each server's `localRoot` status.
261
+ - Tool errors are no longer text-only. Every error result now carries the
262
+ structured `{error}` envelope described above, and the nine tools with an
263
+ output schema publish a JSON Schema draft-07 `oneOf` of their success object
264
+ and that envelope. `ftp_read` keeps text-only success and no output schema;
265
+ its errors still use the same internally validated envelope. Existing text
266
+ responses remain available alongside structured successful responses, and
267
+ `ftp_deploy` still reports bounded samples rather than exhaustive file lists.
268
+ - `ftp_upload`, `ftp_deploy` and `ftp_download` now verify every file before
269
+ promoting it. Uploads hash the local source with SHA256, write to an
270
+ unpredictable sibling named `.ftp-mcp-<random>.tmp`, read that temporary back,
271
+ compare byte count and digest, then promote with one rename. On failure the
272
+ server never deletes the final destination to make promotion succeed and never
273
+ falls back to a direct overwrite; it deletes only its own temporary, and a
274
+ cleanup failure adds a bounded warning that a temporary may remain. Downloads
275
+ read a bounded remote SHA256 expectation first, then promote by same-directory
276
+ hard link with `overwrite:false` (the default) or by rename with
277
+ `overwrite:true`. The basename pattern `.ftp-mcp-*.tmp` is reserved and always
278
+ excluded from `ftp_deploy`, even when an explicit `include` matches it. This
279
+ adds network traffic and latency, does not make a multi-file deployment
280
+ atomic, and does not promise universal atomic replacement: FTP and SFTP rename
281
+ behaviour depends on the server and filesystem. SFTP records the temporary's
282
+ server-assigned mode, restricts it to 0600 while writing, and before promotion
283
+ restores the existing destination's permission bits (0777 mask), or that
284
+ recorded creation mode for a new file; a required stat or chmod failure
285
+ prevents promotion. FTP/FTPS has no portable permission-preservation mechanism
286
+ here, so replacing an existing remote file can reset its permissions to the
287
+ server's creation defaults. Locally, `ftp_download` copies an existing
288
+ destination's 0777 bits onto its temporary, but a **new** local file is now
289
+ created with mode 0600 rather than 0.1.0's process-umask default, and
290
+ promotion with the default `overwrite:false` needs hard-link support in the
291
+ destination directory and fails closed without it. See
292
+ [verified transfers](./docs/TRANSFERS.md).
293
+ - Deployment selection is now bounded and asynchronous. `maxScanEntries` and
294
+ `maxScanDepth` cap visited entries and directory depth; exceeding either
295
+ refuses the complete selection with `SCAN_LIMIT` (`effects:none`,
296
+ `retryable:false`, `next_action:fix_input`) before any remote connection, for
297
+ dry runs as well as real deploys. See
298
+ [resource limits](./docs/RESOURCE-BOUNDS.md).
299
+ - **Deployment selection can now include files it previously skipped.** Only the
300
+ built-in `node_modules`, `.git` and `.ftp-mcp` directory names prune
301
+ traversal. Custom `exclude` patterns and `include` patterns no longer prune
302
+ directories; they keep their existing per-file matching rules. This removes a
303
+ former directory-probe false positive in which, for example,
304
+ `exclude: ["**/__ftp_deploy_probe__"]` also hid `docs/wanted.txt`. Review a
305
+ dry run after upgrading if you rely on exclusion patterns.
306
+ - **BREAKING — plain FTP, and FTPS with `insecureTLS: true`, are now refused.**
307
+ 0.1.0 had no insecure-transport gate and connected to both without objection.
308
+ 0.2.0 raises `TRANSPORT_POLICY` before any network I/O unless the server entry
309
+ sets `allowInsecure: true`, so every 0.1.0 `"protocol": "ftp"` entry — the
310
+ default, namesake protocol of this package — stops connecting until that flag
311
+ is added.
312
+ - **BREAKING — SFTP connections without `hostKeySha256` are refused** before
313
+ authentication, with `HOST_KEY_REJECTED`, unless `allowUnknownHostKey: true`
314
+ is explicitly configured. 0.1.0 performed no host-key verification and had no
315
+ such field, so every existing SFTP server entry stops connecting until one of
316
+ the two keys is added.
317
+ - **BREAKING — FTP/FTPS connections whose `root` is not `/` are refused**
318
+ unless `allowUnsafeRemoteRoot: true` is explicitly configured;
319
+ `REMOTE_ROOT_REJECTED` is raised before any network I/O. 0.1.0 accepted any
320
+ `root` and its own README recommended exactly this pattern
321
+ (`"root": "/var/www/site"`), so those entries stop connecting until the flag
322
+ is added. The recommended boundary is now a dedicated server-side chrooted
323
+ account with `root: "/"`.
324
+ - `ftp_deploy` returns an MCP error when any transfer fails and includes a
325
+ partial-deployment summary, now also as structured `partial` counters. Earlier
326
+ successful transfers are not rolled back.
327
+ - `ftp_list` no longer returns every directory entry by default. It returns the
328
+ first 50 entries and pagination metadata; callers can select an offset and a
329
+ page size from 1 through 200.
330
+ - Tool results are bounded to 25000 bytes of UTF-8 `JSON.stringify` covering
331
+ both text and structured data after redaction. Oversized results shrink their
332
+ bounded samples and rebuild pagination first, then truncate the largest text
333
+ body with a `… [output truncated]` marker, and fall back to `OUTPUT_LIMIT`
334
+ only when nothing can be shrunk — while preserving the request UUID, effects
335
+ and observed partial counters. This cap sits below `ftp_read`'s unchanged
336
+ `max_bytes` window (default 262144, hard max 1048576): a read that returned
337
+ 256 KB of content on 0.1.0 now returns roughly 25 KB, marked as truncated, in
338
+ a successful result.
339
+ - Boolean safety flags and SFTP fingerprint formats are validated per server.
340
+ - `setup` surfaces active insecure-transport acknowledgments; `doctor` surfaces
341
+ active insecure-transport, unknown-host-key and unsafe-remote-root
342
+ acknowledgments.
343
+ - Documentation now distinguishes the working source install from future
344
+ `npx` and MCP registry installation.
345
+ - The published package now also ships `docs/`, `evaluations/`, `server.json`,
346
+ `CONTRIBUTING.md`, `CONTRIBUTING.fr.md`, `SECURITY.md`, `SECURITY.fr.md`,
347
+ `CHANGELOG.fr.md` and `LICENSE.fr.md`. The package and `server.json`
348
+ descriptions are now bilingual and describe local confinement and verified
349
+ staging.
350
+ - New runtime dependency `zod-to-json-schema`, used to publish the tool input
351
+ and output JSON Schemas.
352
+ - The local FTP test server now uses the `@electerm/ftp-srv` development
353
+ dependency.
354
+
355
+ ### Removed
356
+
357
+ - `assets/banner.svg`, `assets/banner.png`, `assets/demo.svg` and
358
+ `assets/diagram.svg`. The README banners are now `assets/banner-en.png` and
359
+ `assets/banner-fr.png`, with their generation prompts and provenance recorded
360
+ under `assets/provenance/`.
361
+ - The `ftp-srv` development dependency, replaced as noted above.
362
+
363
+ ### Fixed
364
+
365
+ - Interactive manual server entry in `setup` no longer writes the wizard result
366
+ through a missing property.
367
+ - Case variants of protocol names can no longer bypass insecure-transport
368
+ checks on setup and diagnostic paths.
369
+ - Tool errors retain applicable security warnings, including failures after an
370
+ insecure connection may already have exposed credentials.
371
+
372
+ ### Security
373
+
374
+ - Insecure transports are refused by default; see the **BREAKING** entry under
375
+ *Changed* for the new `allowInsecure` gate and its upgrade impact. Warnings
376
+ are attached to discovery, diagnostics, successes, and failures involving
377
+ accepted insecure transports.
378
+ - SFTP resolves the configured remote root and uses realpath/lstat checks to
379
+ refuse symbolic-link components and resolved paths outside that root. A
380
+ malicious server can still race state between validation and operation. An
381
+ SFTP connection also pins its initial canonical root; a known root change
382
+ returns `TARGET_CHANGED` and refuses to rebase an owned temporary.
383
+ - FTP/FTPS no longer describe lexical path normalization as a trustworthy
384
+ anti-symlink jail; server-side account isolation is the real boundary.
385
+ - Local sources and download destinations are constrained under `localRoot`.
386
+ - Non-interactive setup fails closed: it reports insecure transports but never
387
+ grants an `allowInsecure` exception automatically.
388
+ - Error confidentiality is enforced at the result boundary. Error objects,
389
+ causes, stacks, credentials and raw arguments are never serialized; secrets
390
+ are masked in free text; configuration credentials are collected before
391
+ validation, including values from rejected entries and resolved `${ENV:VAR}`
392
+ placeholders, so loader diagnostics use the same mask. Malformed configuration
393
+ JSON yields a generic syntax diagnostic with no parser excerpt, and rejected
394
+ FileZilla blocks are identified by numeric index without echoing untrusted
395
+ names or protocol fields. Remote strings such as `Page:` and `SECURITY
396
+ WARNING` cannot impersonate the trusted notice rendering.
397
+ - The committed lockfile resolves the MCP SDK's transitive `hono` dependency to
398
+ the patched 4.13.5.
399
+
400
+ ## [0.1.0] - 2026-07-20
401
+
402
+ ### Added
403
+
404
+ - Ten MCP tools: `ftp_list_servers`, `ftp_test`, `ftp_list`, `ftp_read`,
405
+ `ftp_upload`, `ftp_deploy`, `ftp_download`, `ftp_mkdir`, `ftp_rename`, and
406
+ `ftp_delete`.
407
+ - Multi-server configuration across FTP, FTPS, and SFTP.
408
+ - Recursive directory deploy with gitignore-like excludes and dry-run.
409
+ - Per-server remote `root` path normalization and `readOnly` mode.
410
+ - `${ENV:VAR}` placeholders for configuration secrets.
411
+ - FileZilla import, including implicit FTPS sites.
412
+ - Setup wizard with MCP client configuration backups.
413
+ - Read-only `doctor` diagnostic and source installers.
414
+ - English and French documentation.
415
+ - End-to-end tests against local FTP and SFTP servers.
@@ -0,0 +1,120 @@
1
+ # Contribuer à ftp-deploy-mcp
2
+
3
+ [English](./CONTRIBUTING.md) | **Français**
4
+
5
+ Merci d'envisager une contribution. Ce projet reste volontairement compact et
6
+ limite ses dépendances ; lisez les principes ci-dessous avant d'ouvrir une PR.
7
+
8
+ Le runtime MCP prend en charge Node.js >=22. L'outillage de publication exige **Node.js 24.20.0 exactement** : création et inspection des archives, inventaires source, contrôles du graphe installé, vérification du registre et `npm run test:release` refusent les autres versions avant ces opérations. Utilisez la distribution Node qualifiée, avec son npm fourni, et consignez les versions Node/libuv/npm/tar avec `node scripts/release-toolchain.mjs`. La qualification du consommateur peut toujours exécuter le serveur MCP avec Node 22 et appeler séparément les outils externes de publication avec Node 24.20.0.
9
+
10
+ La CI conserve les neuf jobs runtime (Node 22/24/26 sur Linux, macOS et Windows). La suite complète de publication s'exécute séparément sur les trois systèmes avec Node 24.20.0. macOS comporte trois passes complètes prédéfinies ; tout échec fait échouer le job. Ces passes de qualification sont fixes, sans relance après échec, cas ignoré ni augmentation des limites d'archive. Les workflows supply-chain et de publication npm/MCP utilisent la même version Node exacte.
11
+
12
+ ## Environnement de développement
13
+
14
+ ```bash
15
+ git clone https://github.com/alebgl77/ftp-deploy-mcp.git
16
+ cd ftp-deploy-mcp
17
+ npm ci --ignore-scripts
18
+ npm test
19
+ ```
20
+
21
+ `npm test` est le principal contrôle du code. Il démarre de vrais serveurs FTP
22
+ et SFTP locaux sur des ports de boucle locale et exécute la suite de bout en
23
+ bout contre eux : aucun accès réseau externe n'est requis ni utilisé. Pour
24
+ les modifications de documentation seules, vérifiez aussi que les exemples
25
+ JSON modifiés s'analysent, que les liens Markdown relatifs existent, puis
26
+ exécutez `git diff --check`.
27
+
28
+ ## Mettre à jour le graphe de dépendances
29
+
30
+ `package-lock.json` est l'unique fichier de verrouillage de référence ;
31
+ n'ajoutez pas de `npm-shrinkwrap.json` parallèle. Les six dépendances directes de
32
+ production sont épinglées à une version exacte. L'installation courante utilise
33
+ `npm ci --ignore-scripts` et ne doit pas réécrire le graphe.
34
+
35
+ Pour une mise à jour voulue, modifiez uniquement les versions exactes approuvées,
36
+ régénérez le verrouillage source avec les scripts de cycle de vie désactivés et la version
37
+ npm qualifiée, puis consignez cette version npm. Examinez toute entrée du graphe
38
+ ajoutée, supprimée ou modifiée, notamment `resolved`, `integrity`, les contraintes
39
+ facultatives/de plateforme et les hooks d'installation. N'acceptez pas de
40
+ modifications sans rapport issues d'une mise à jour ou d'une correction d'audit
41
+ automatique. Exécutez une installation propre, les tests concernés, l'audit des
42
+ dépendances et les contrôles de l'archive exacte et du consommateur isolé du
43
+ [guide de publication](./docs/RELEASE.fr.md). Présentez ensemble les modifications
44
+ des sources, du manifeste et du verrouillage source pour la revue.
45
+
46
+ La distribution d'installation source contient 90 fichiers, dont ce verrouillage. Le paquet npm distinct contient 89 fichiers sans verrouillage et reste non qualifié après dix dérives observées dans une installation réelle. Les contrôles du graphe installé comparent aussi les dépendances transitives et les peers à l'inventaire source original ; tout écart bloque la publication npm/MCP.
47
+
48
+ ## Principes
49
+
50
+ - **JavaScript ESM simple.** Pas de TypeScript, d'outil de regroupement ni
51
+ d'étape de compilation. Le contenu de `src/` est exécuté tel quel.
52
+ - **Aucune nouvelle dépendance d'exécution sans discussion.** Ouvrez d'abord
53
+ une issue si vous en jugez une nécessaire : les `dependencies` de
54
+ `package.json` sont volontairement limitées.
55
+ - **Chaque fonctionnalité s'accompagne d'assertions de test.** Les nouveaux
56
+ outils, options ou comportements ne sont terminés qu'une fois couverts
57
+ dans `test/smoke.test.js`.
58
+ - **Les garanties de sécurité reflètent les limites des protocoles.** Les
59
+ sous-racines FTP/FTPS côté client ne sont pas présentées comme un confinement
60
+ sûr contre les liens symboliques ; le compte/chroot dédié côté serveur est
61
+ la frontière. Les protections SFTP doivent documenter la course résiduelle
62
+ côté serveur.
63
+ - **Disponibilité des sources et disponibilité d'une publication sont
64
+ distinctes.** N'annoncez pas l'installation par `npx` ou le registre MCP
65
+ avant que l'artefact correspondant soit public et vérifié indépendamment.
66
+ - **En mode serveur, `stdout` est réservé à JSON-RPC.** N'utilisez jamais
67
+ `console.log` dans le chemin d'exécution du serveur MCP : toute sortie
68
+ standard est un message du protocole. Les diagnostics et les messages
69
+ destinés aux humains vont sur `stderr` ou dans les commandes `doctor`/`setup`
70
+ (hors mode serveur).
71
+
72
+ ## Exécuter une partie de la suite
73
+
74
+ Exécutez la suite principale :
75
+
76
+ ```bash
77
+ node test/smoke.test.js
78
+ ```
79
+
80
+ Exécutez séparément la qualification des transports et les contrôles de publication :
81
+
82
+ ```bash
83
+ node --test test/transport-qualification.js
84
+ npm run test:release
85
+ ```
86
+
87
+ `npm test` exécute la suite principale et la qualification des transports.
88
+ Avant d'ouvrir une PR, lancez les deux ainsi que les contrôles de publication
89
+ concernés par le changement ; gardez les assertions existantes activées.
90
+
91
+ ## Liste de contrôle d'une PR
92
+
93
+ - [ ] `npm test` passe (utilisez le total actuel ; ne le figez pas dans la documentation).
94
+ - [ ] Aucune nouvelle dépendance d'exécution, ou elle a d'abord été discutée dans une issue.
95
+ - [ ] Les comportements ajoutés ou modifiés ont des assertions de test correspondantes.
96
+ - [ ] `README.md` (anglais) et `README.fr.md` (français) sont mis à jour si le
97
+ comportement visible par l'utilisateur a changé.
98
+ - [ ] Les nouveaux exemples de configuration sont en JSON strict et les liens
99
+ Markdown relatifs pointent vers des cibles existantes.
100
+ - [ ] Les modifications sensibles pour la sécurité figurent dans
101
+ [docs/SECURITY-MODEL.fr.md](./docs/SECURITY-MODEL.fr.md).
102
+
103
+ ## Publication (mainteneurs)
104
+
105
+ N'improvisez pas la première publication à partir de cette courte section.
106
+ Suivez le [guide de publication](./docs/RELEASE.fr.md), qui couvre :
107
+
108
+ - la concordance des versions du paquet, du verrouillage source, du serveur et du tag,
109
+ puis la vérification distincte des versions npm et du registre MCP officiel
110
+ après publication ;
111
+ - la validation d'une archive propre et les tests de bout en bout ;
112
+ - npm Trusted Publishing, la provenance et le recours à un `NPM_TOKEN` de
113
+ courte durée uniquement si l'amorçage de la première publication l'exige ;
114
+ - les opérations manuelles de propriété et de publication sur le registre MCP ;
115
+ - les vérifications après publication et le rétablissement par une nouvelle
116
+ version corrective.
117
+
118
+ Le paquet npm et l'entrée du registre MCP ne sont disponibles qu'une fois
119
+ tous les prérequis manuels applicables et toutes les vérifications de ce
120
+ guide satisfaits.
@@ -0,0 +1,111 @@
1
+ # Contributing to ftp-deploy-mcp
2
+
3
+ **English** | [Français](./CONTRIBUTING.fr.md)
4
+
5
+ Thanks for considering a contribution. This project intentionally stays small and
6
+ dependency-light — please read the principles below before opening a PR.
7
+
8
+ The MCP runtime supports Node.js >=22. Release tooling requires **Node.js 24.20.0 exactly**: archive creation and inspection, source inventories, installed-graph checks, registry verification and `npm run test:release` reject other versions before performing those operations. Use the qualified Node distribution, including its bundled npm, and record Node/libuv/npm/tar versions with `node scripts/release-toolchain.mjs`. Consumer runtime qualification may still execute the MCP server with Node 22 while invoking the external release tools separately with Node 24.20.0.
9
+
10
+ CI retains all nine runtime jobs (Node 22/24/26 on Linux, macOS and Windows). The complete release suite runs separately on all three operating systems with Node 24.20.0. macOS has three predefined full passes; any failed pass fails the job. These are fixed qualification passes, with no failure retries, skipped cases or increased archive limits. Supply-chain and npm/MCP publication workflows use the same exact release Node version.
11
+
12
+ ## Dev setup
13
+
14
+ ```bash
15
+ git clone https://github.com/alebgl77/ftp-deploy-mcp.git
16
+ cd ftp-deploy-mcp
17
+ npm ci --ignore-scripts
18
+ npm test
19
+ ```
20
+
21
+ `npm test` is the main code gate. It spins up real local FTP and SFTP servers
22
+ on loopback ports and runs the full e2e suite against them — no external
23
+ network access is required or used. Documentation-only changes should also
24
+ parse changed JSON examples, check relative Markdown links, and run
25
+ `git diff --check`.
26
+
27
+ ## Updating the dependency graph
28
+
29
+ `package-lock.json` is the only authoritative lockfile; do not add a parallel
30
+ `npm-shrinkwrap.json`. The six direct production dependencies are exact pins.
31
+ Routine setup uses `npm ci --ignore-scripts` and must not rewrite the graph.
32
+
33
+ For an intentional update, change only the approved exact versions, regenerate
34
+ the source lockfile with lifecycle scripts disabled and the qualified npm version,
35
+ and record that npm version. Review every added, removed or changed graph entry,
36
+ including `resolved`, `integrity`, optional/platform constraints and install
37
+ hooks. Do not accept unrelated graph changes from an automatic update or audit
38
+ fix. Run a clean install, the affected tests, the dependency audit and exact
39
+ tarball/isolated-consumer checks in the [release guide](./docs/RELEASE.md).
40
+ Keep the source, manifest and lockfile changes together for review.
41
+
42
+ The source installation distribution contains 90 files, including this lockfile. The separate npm package contains 89 files without a lockfile and remains unqualified after ten observed changes in a real installation. Installed-graph checks compare transitive dependencies and peers against the original source inventory; any difference blocks npm/MCP publication.
43
+
44
+ ## Principles
45
+
46
+ - **Plain ESM JavaScript.** No TypeScript, no bundler, no build step. What's in
47
+ `src/` is what runs.
48
+ - **No new runtime dependencies without discussion.** Open an issue first if you
49
+ think one is needed — `dependencies` in `package.json` are kept deliberately
50
+ minimal.
51
+ - **Every feature lands with smoke-test assertions.** New tools, options, or
52
+ behaviors are not considered done until they're covered in
53
+ `test/smoke.test.js`.
54
+ - **Security claims match protocol reality.** FTP/FTPS client-side sub-roots
55
+ are not described as a symlink-safe jail; a dedicated server-side
56
+ account/chroot is the boundary. SFTP protections must document the residual
57
+ server-side race.
58
+ - **Source and published availability are distinct.** Do not advertise `npx`
59
+ or MCP registry installation until the corresponding artifact is public and
60
+ independently verified.
61
+ - **`stdout` in server mode is JSON-RPC only.** Never `console.log` from the MCP
62
+ server path — anything written to stdout is a wire-protocol message.
63
+ Diagnostics and human-facing output belong on `stderr` or in `doctor`/`setup`
64
+ (non-server) commands.
65
+
66
+ ## Running part of the suite
67
+
68
+ Run the main smoke suite:
69
+
70
+ ```bash
71
+ node test/smoke.test.js
72
+ ```
73
+
74
+ Run the transport qualification and release gates separately:
75
+
76
+ ```bash
77
+ node --test test/transport-qualification.js
78
+ npm run test:release
79
+ ```
80
+
81
+ `npm test` runs the main smoke suite and transport qualification. Before
82
+ opening a PR, run both and any release gates affected by the change; keep
83
+ existing assertions enabled.
84
+
85
+ ## PR checklist
86
+
87
+ - [ ] `npm test` passes (use the current total; do not hard-code it in docs).
88
+ - [ ] No new runtime dependency, or it was discussed in an issue first.
89
+ - [ ] New/changed behavior has matching smoke-test assertions.
90
+ - [ ] Docs updated in both `README.md` (English) and `README.fr.md` (French) if
91
+ user-facing behavior changed.
92
+ - [ ] New configuration examples are strict JSON and relative Markdown links
93
+ resolve.
94
+ - [ ] Security-sensitive changes are reflected in
95
+ [docs/SECURITY-MODEL.md](./docs/SECURITY-MODEL.md).
96
+
97
+ ## Releasing (maintainers)
98
+
99
+ Do not improvise the first publication from this short section. Follow the
100
+ [release guide](./docs/RELEASE.md), which covers:
101
+
102
+ - matching package, lockfile, server and tag versions, then separately
103
+ verifying npm and Official MCP Registry versions after publication;
104
+ - clean-tarball validation and end-to-end tests;
105
+ - npm Trusted Publishing/provenance and the short-lived `NPM_TOKEN` fallback
106
+ needed only when first-publication bootstrapping requires it;
107
+ - manual MCP registry ownership and publication;
108
+ - post-publish verification and fix-forward rollback.
109
+
110
+ The npm package and MCP registry entry are not available until every applicable
111
+ manual prerequisite and verification step in that guide has passed.