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.
- package/CHANGELOG.fr.md +468 -0
- package/CHANGELOG.md +415 -0
- package/CONTRIBUTING.fr.md +120 -0
- package/CONTRIBUTING.md +111 -0
- package/LICENSE +21 -0
- package/LICENSE.fr.md +26 -0
- package/README.fr.md +518 -0
- package/README.md +501 -2
- package/SECURITY.fr.md +99 -0
- package/SECURITY.md +94 -0
- package/docs/AGENT-PERFORMANCE.fr.md +144 -0
- package/docs/AGENT-PERFORMANCE.md +133 -0
- package/docs/ERROR-CONTRACT.fr.md +141 -0
- package/docs/ERROR-CONTRACT.md +137 -0
- package/docs/LANGUAGES.fr.md +68 -0
- package/docs/LANGUAGES.md +62 -0
- package/docs/RELEASE.fr.md +462 -0
- package/docs/RELEASE.md +435 -0
- package/docs/RESOURCE-BOUNDS.fr.md +144 -0
- package/docs/RESOURCE-BOUNDS.md +132 -0
- package/docs/SCRIPTED-EVALUATIONS.fr.md +190 -0
- package/docs/SCRIPTED-EVALUATIONS.md +181 -0
- package/docs/SECURITY-MODEL.fr.md +262 -0
- package/docs/SECURITY-MODEL.md +236 -0
- package/docs/STATE-STORAGE.fr.md +236 -0
- package/docs/STATE-STORAGE.md +231 -0
- package/docs/TRANSFERS.fr.md +140 -0
- package/docs/TRANSFERS.md +123 -0
- package/docs/WORKFLOW-MODEL.fr.md +63 -0
- package/docs/WORKFLOW-MODEL.md +62 -0
- package/evaluations/README.fr.md +95 -0
- package/evaluations/README.md +92 -0
- package/evaluations/fixture/README.txt +3 -0
- package/evaluations/fixture/catalog/alpha.txt +4 -0
- package/evaluations/fixture/catalog/bravo.txt +4 -0
- package/evaluations/fixture/catalog/charlie.txt +4 -0
- package/evaluations/fixture/catalog/delta.txt +4 -0
- package/evaluations/fixture/catalog/echo.txt +4 -0
- package/evaluations/fixture/catalog/foxtrot.txt +4 -0
- package/evaluations/fixture/reports/checks.txt +4 -0
- package/evaluations/fixture/reports/release.txt +3 -0
- package/evaluations/read-only.fr.xml +42 -0
- package/evaluations/read-only.xml +42 -0
- package/ftp-servers.example.json +27 -0
- package/install.cmd +29 -0
- package/install.sh +18 -0
- package/package.json +88 -4
- package/server.json +35 -0
- package/src/adapters/ftp.js +392 -0
- package/src/adapters/sftp.js +647 -0
- package/src/admission.js +15 -0
- package/src/atomic-write.js +131 -0
- package/src/clients.js +212 -0
- package/src/config.js +629 -0
- package/src/durable/confirmation.js +88 -0
- package/src/durable/engine.js +696 -0
- package/src/durable/plan.js +238 -0
- package/src/durable/schemas.js +206 -0
- package/src/durable/state.js +317 -0
- package/src/durable/status.js +175 -0
- package/src/durable-tools.js +94 -0
- package/src/errors.js +88 -0
- package/src/filezilla.js +260 -0
- package/src/i18n.js +58 -0
- package/src/index.js +175 -0
- package/src/local-path.js +130 -0
- package/src/locales/en.js +152 -0
- package/src/locales/errors.en.js +22 -0
- package/src/locales/errors.fr.js +22 -0
- package/src/locales/fr.js +152 -0
- package/src/locales/runtime.en.js +259 -0
- package/src/locales/runtime.fr.js +259 -0
- package/src/operations.js +276 -0
- package/src/redact.js +105 -0
- package/src/remote-path.js +101 -0
- package/src/scanner.js +103 -0
- package/src/setup.js +888 -0
- package/src/state/codec.mjs +103 -0
- package/src/state/index.mjs +1 -0
- package/src/state/io.mjs +173 -0
- package/src/state/records.mjs +60 -0
- package/src/state/store.mjs +360 -0
- package/src/state/writer.mjs +250 -0
- package/src/tool-registry.js +464 -0
- package/src/tools.js +1226 -0
- package/src/transfers.js +425 -0
- package/src/workflow/budget.mjs +83 -0
- package/src/workflow/events.mjs +199 -0
- package/src/workflow/model.mjs +179 -0
package/docs/RELEASE.md
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
# Release Guide
|
|
2
|
+
|
|
3
|
+
**English** | [Français](./RELEASE.fr.md)
|
|
4
|
+
|
|
5
|
+
This is the maintainer checklist for the GitHub source release v0.3.1 and,
|
|
6
|
+
separately, a future first npm and Official MCP Registry publication. A GitHub
|
|
7
|
+
release does not make the npm identifier, `npx` command or registry entry
|
|
8
|
+
available. The published GitHub v0.3.0 tag and release remain immutable.
|
|
9
|
+
|
|
10
|
+
Check available source assets on [GitHub Releases](https://github.com/alebgl77/ftp-deploy-mcp/releases/latest).
|
|
11
|
+
The npm and MCP workflows are manual (`workflow_dispatch`); do not dispatch
|
|
12
|
+
them as part of a source-only GitHub release.
|
|
13
|
+
|
|
14
|
+
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.
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
|
|
18
|
+
Pull requests also run dependency review and reject newly introduced high or
|
|
19
|
+
critical vulnerabilities. CodeQL analyzes JavaScript/TypeScript on pushes,
|
|
20
|
+
pull requests and a weekly schedule. The supply-chain job uploads its validated
|
|
21
|
+
CycloneDX 1.5 production SBOM as a 14-day CI artifact only after its complete
|
|
22
|
+
package identity set matches `npm ls --omit=dev --all`; the SBOM is not added
|
|
23
|
+
to npm packages or publication artifacts. The SBOM gate compares identities
|
|
24
|
+
rather than CycloneDX edge layout because npm may hoist or deduplicate paths;
|
|
25
|
+
the separate release-graph gate validates the installed production graph
|
|
26
|
+
against the authoritative lockfile.
|
|
27
|
+
|
|
28
|
+
## Manual prerequisites
|
|
29
|
+
|
|
30
|
+
Complete these outside the repository before dispatching npm or MCP publication:
|
|
31
|
+
|
|
32
|
+
- Confirm control of the intended npm package name and maintainer access to the
|
|
33
|
+
npm account or organization.
|
|
34
|
+
- Enforce npm two-factor authentication and use a dedicated maintainer account
|
|
35
|
+
with least privilege.
|
|
36
|
+
- For a package that already exists, configure npm Trusted Publishing for
|
|
37
|
+
`alebgl77/ftp-deploy-mcp` and workflow filename `release.yml`. No named GitHub
|
|
38
|
+
environment is configured by these workflows.
|
|
39
|
+
- For the **first** publication, npm requires the package to exist before a
|
|
40
|
+
trust relationship can be configured. A maintainer must create a short-lived
|
|
41
|
+
granular token with the minimum available publish scope and the required
|
|
42
|
+
non-interactive/2FA permission, and store it only as `NPM_TOKEN` in GitHub
|
|
43
|
+
Actions secrets. After the first successful publish, configure Trusted
|
|
44
|
+
Publishing, remove that secret, and revoke the token immediately. Never
|
|
45
|
+
commit, paste into an issue/chat, or print the token. Do not create a
|
|
46
|
+
persistent token as a workaround. See [npm trust prerequisites](https://docs.npmjs.com/cli/v11/commands/npm-trust/)
|
|
47
|
+
and [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).
|
|
48
|
+
- Confirm that the release workflow requests npm provenance and has only the
|
|
49
|
+
permissions it needs. Repository automation cannot create npm ownership or
|
|
50
|
+
approve a new package name.
|
|
51
|
+
- Establish the publisher identity and namespace required by the official MCP
|
|
52
|
+
registry, and confirm access to its current publishing tool. A prepared
|
|
53
|
+
manifest or third-party index listing is not a registry publication.
|
|
54
|
+
- Confirm that GitHub environments, required reviewers, branch protection, and
|
|
55
|
+
release permissions are in place.
|
|
56
|
+
|
|
57
|
+
Record who completed each manual prerequisite and when. Do not start npm or MCP
|
|
58
|
+
publication while any ownership or credential step is unresolved. These registry
|
|
59
|
+
prerequisites do not establish whether a GitHub source release is ready.
|
|
60
|
+
|
|
61
|
+
## Version and changelog gate
|
|
62
|
+
|
|
63
|
+
For the v0.3.1 GitHub release:
|
|
64
|
+
|
|
65
|
+
1. Set `version` in [package.json](../package.json), the root and root-package
|
|
66
|
+
metadata of `package-lock.json`, and `server.json` to `0.3.1`. The source lockfile
|
|
67
|
+
is the sole authoritative lockfile; do not keep a parallel `npm-shrinkwrap.json`.
|
|
68
|
+
2. Set the MCP server's reported version to the same value. Search the source
|
|
69
|
+
and generated metadata for stale release strings; expected historical
|
|
70
|
+
references in the changelog are exempt.
|
|
71
|
+
3. Use the paired heading `## [0.3.1] - 2026-10-17` in
|
|
72
|
+
[CHANGELOG.md](../CHANGELOG.md) and [CHANGELOG.fr.md](../CHANGELOG.fr.md).
|
|
73
|
+
Confirm the effective release date is a valid calendar date, update it only
|
|
74
|
+
if the actual release day changes, and preserve the complete 0.3.0 history.
|
|
75
|
+
4. Confirm every user-visible minor-release change is documented in both
|
|
76
|
+
[README.md](../README.md) and [README.fr.md](../README.fr.md).
|
|
77
|
+
5. Verify the release includes atomic replacement for newly written sensitive
|
|
78
|
+
configuration and tests its failure path. This is a release gate, not a
|
|
79
|
+
documentation-only claim.
|
|
80
|
+
6. Confirm `node src/index.js --version`, package metadata, source lockfile and tag
|
|
81
|
+
agree. Verify npm and Official MCP Registry versions separately only after
|
|
82
|
+
their respective publications.
|
|
83
|
+
|
|
84
|
+
Use a dedicated release commit. Never move or reuse the published v0.2.0 tag.
|
|
85
|
+
|
|
86
|
+
## Review the source and installed dependency graphs
|
|
87
|
+
|
|
88
|
+
The sole source authority is `package-lock.json`, with six exact direct pins and
|
|
89
|
+
112 non-root records: 110 production and two development. Source/Docker/user
|
|
90
|
+
installs use `npm ci --omit=dev --ignore-scripts`; contributors use
|
|
91
|
+
`npm ci --ignore-scripts`. No shrinkwrap is retained. npm 12 no longer reads or
|
|
92
|
+
writes shrinkwrap files, including in dependencies; see the
|
|
93
|
+
[official npm lockfile documentation](https://docs.npmjs.com/cli/v12/configuring-npm/package-lock-json/).
|
|
94
|
+
|
|
95
|
+
For an approved dependency update, change only the intended exact versions and
|
|
96
|
+
regenerate the source lock with scripts disabled. Record Node/npm versions and
|
|
97
|
+
review every graph change, registry URL, integrity value and optional/platform
|
|
98
|
+
constraint. Keep the original reviewed source inventory as the authority when
|
|
99
|
+
checking installations; never update it from the installed graph.
|
|
100
|
+
|
|
101
|
+
The built-in-only graph verifier reads actual package manifests and installed
|
|
102
|
+
lock records, including the hidden lock when present. It resolves transitive
|
|
103
|
+
dependencies and peers from their real locations, including hoisted and nested
|
|
104
|
+
packages. It rejects version/resolution/integrity drift, undeclared extras,
|
|
105
|
+
missing required packages and changed dependency declarations. Declared optional
|
|
106
|
+
omissions and source-proven development packages are reported separately; a
|
|
107
|
+
present optional dependency with changed bytes or metadata is not an omission.
|
|
108
|
+
No dependency code is imported by this verifier.
|
|
109
|
+
|
|
110
|
+
## Qualify the source installation archive
|
|
111
|
+
|
|
112
|
+
There are two fixed distributions. `ftp-deploy-mcp-0.3.1-source.tar.gz` contains
|
|
113
|
+
90 regular files under `package/`, including the source lock. It is an
|
|
114
|
+
installation archive, not the complete repository: tests, maintainer scripts and
|
|
115
|
+
the HTML guide remain in the repository. GitHub's automatic source ZIP/tar
|
|
116
|
+
contains the complete repository. The separate npm archive
|
|
117
|
+
`ftp-deploy-mcp-0.3.1.tgz` contains 89 files and no lockfile.
|
|
118
|
+
|
|
119
|
+
Capture all 90 reviewed source files before installation, tests or packing.
|
|
120
|
+
Keep the portable inventory outside the checkout and retain its original SHA256
|
|
121
|
+
independently of any generated proof. A source-only snapshot qualifies a pre-tag
|
|
122
|
+
candidate; publication requires the actual clean tracked tag/event checkout.
|
|
123
|
+
For a pre-tag candidate, run from the intended checkout (Bash):
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
source_root="$(pwd -P)"
|
|
127
|
+
release_tmp="$(mktemp -d)"
|
|
128
|
+
inventory="$release_tmp/source-inventory.json"
|
|
129
|
+
node scripts/release-artifact.mjs snapshot "$inventory" --source-root "$source_root" --source-only
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Record the original snapshot output in `inventory_sha256` before continuing.
|
|
133
|
+
Never replace it with a value reread from a rewritten proof.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npm ci --ignore-scripts
|
|
137
|
+
npm test
|
|
138
|
+
npm run test:release
|
|
139
|
+
node scripts/release-gate.mjs --source-only --runtime
|
|
140
|
+
node scripts/release-artifact.mjs build-source "$release_tmp/source-pack.json" --source-root "$source_root" --inventory "$inventory" --inventory-sha256 "$inventory_sha256" --source-only
|
|
141
|
+
node scripts/release-artifact.mjs preflight "$release_tmp/source-pack.json" --source-root "$source_root" --inventory "$inventory" --inventory-sha256 "$inventory_sha256" --distribution source
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The builder writes only captured bytes into a private staging directory and uses
|
|
145
|
+
real tar with bounded stdout. It validates every byte before exclusive archive
|
|
146
|
+
and metadata writes. Retain the preflight's original canonical `tarball` path
|
|
147
|
+
and SHA512 `integrity` outputs in those variables, then install in a fresh folder:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
mkdir "$release_tmp/consumer"
|
|
151
|
+
tar --ignore-zeros -xzf "$tarball" -C "$release_tmp/consumer"
|
|
152
|
+
(cd "$release_tmp/consumer/package" && npm ci --omit=dev --ignore-scripts)
|
|
153
|
+
node scripts/release-graph.mjs --source-root "$source_root" --inventory "$inventory" --inventory-sha256 "$inventory_sha256" --product-root "$release_tmp/consumer/package" --install-root "$release_tmp/consumer/package" --output "$release_tmp/source-graph.json" --source-only
|
|
154
|
+
npm audit --prefix "$release_tmp/consumer/package" --omit=dev --audit-level=moderate
|
|
155
|
+
node scripts/release-artifact.mjs check "$release_tmp/source-pack.json.verified.json" --source-root "$source_root" --inventory "$inventory" --inventory-sha256 "$inventory_sha256" --expected-integrity "$integrity" --expected-tarball "$tarball" --distribution source --source-only
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Run the independent consumer runtime/transport cases, audits and production SBOM
|
|
159
|
+
checks against this extracted installation before approving it. The installed
|
|
160
|
+
product needs no `.git`; its Git provenance comes from the external checkout and
|
|
161
|
+
original inventory. Never install the source tar.gz as an npm package. Users
|
|
162
|
+
extract `package/`, run `npm ci --omit=dev --ignore-scripts` there, then run
|
|
163
|
+
`node src/index.js setup` explicitly. Contributor commands run from the full
|
|
164
|
+
repository, not from this installation archive.
|
|
165
|
+
|
|
166
|
+
The proof binds an explicit `source` or `npm` distribution, exact filename,
|
|
167
|
+
`package/` prefix, scope, commit, inventory SHA256, original integrity and
|
|
168
|
+
canonical artifact path. The controller supplies the expected distribution;
|
|
169
|
+
`inspect`/`check` default to npm and refuse a source proof. Changing a proof or
|
|
170
|
+
renaming an archive cannot change its distribution.
|
|
171
|
+
|
|
172
|
+
## Keep npm qualification separate
|
|
173
|
+
|
|
174
|
+
npm remains unqualified: a real npm 10.9.8 consumer changed ten production
|
|
175
|
+
dependencies. Its failed candidate and reports are retained, never promoted to
|
|
176
|
+
source-release evidence or attached as a qualified asset. A future matching npm
|
|
177
|
+
graph would establish only the tested environment/date, not reproducibility of
|
|
178
|
+
future npm installs. To inspect a separate npm candidate with the original
|
|
179
|
+
90-file source inventory:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
npm pack --ignore-scripts --json --pack-destination "$release_tmp" > "$release_tmp/npm-pack.json"
|
|
183
|
+
node scripts/release-artifact.mjs preflight "$release_tmp/npm-pack.json" --source-root "$source_root" --inventory "$inventory" --inventory-sha256 "$inventory_sha256" --distribution npm
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Inspection checks all 89 npm files against the source; the source lock remains
|
|
187
|
+
external authority. Install this exact npm candidate in an isolated prefix with
|
|
188
|
+
scripts disabled, then invoke `release-graph.mjs` with `--product-root` pointing
|
|
189
|
+
to `<prefix>/node_modules/ftp-deploy-mcp` and `--install-root` to the prefix.
|
|
190
|
+
Any graph difference must fail before smoke tests, artifact transfer or npm/MCP
|
|
191
|
+
publication. A source-only pass never authorizes a registry publication.
|
|
192
|
+
|
|
193
|
+
## Artifact validation and provenance limits
|
|
194
|
+
|
|
195
|
+
The fixed allowlists exclude local configuration, credentials, tests, temporary
|
|
196
|
+
files and maintainer state. Complete package metadata and the source lock are
|
|
197
|
+
structurally validated; each shipped byte must match the original inventory.
|
|
198
|
+
Publication commands receive real `GITHUB_REF` and `GITHUB_SHA` from GitHub;
|
|
199
|
+
HEAD and the exact tag target must match that event. Never invent this context.
|
|
200
|
+
Use `snapshot` without `--source-only`, `inspect` instead of `preflight`, and
|
|
201
|
+
`check` without `--source-only` only in that real publication context.
|
|
202
|
+
|
|
203
|
+
Compressed bytes are read once with an 8 MiB cap and provide the original SHA512.
|
|
204
|
+
Strict built-in gzip decoding has a 32 MiB total output cap, including TAR headers
|
|
205
|
+
and padding. `engine.bytesWritten` must be an integer within the input length;
|
|
206
|
+
any unconsumed byte must be NUL. Terminal NUL padding is allowed, nonzero hidden
|
|
207
|
+
tails are rejected. Every inspection tar call receives the same decoded raw TAR,
|
|
208
|
+
without `-z`, and uses `--ignore-zeros`. Untrusted archive paths are never
|
|
209
|
+
extracted to disk during inspection.
|
|
210
|
+
|
|
211
|
+
| Inspection limit | Maximum |
|
|
212
|
+
|---|---|
|
|
213
|
+
| Compressed archive | 8 MiB |
|
|
214
|
+
| Complete decoded TAR, headers and padding included | 32 MiB |
|
|
215
|
+
| Each expected or extracted file | 1 MiB |
|
|
216
|
+
| Complete inspection | 90 seconds |
|
|
217
|
+
| Each tar invocation | 15 seconds, or the shorter remaining budget |
|
|
218
|
+
|
|
219
|
+
These are input/output/time limits, not an exact memory bound. The deadline is
|
|
220
|
+
checked before and after synchronous gzip decoding and cannot preempt it. Failed
|
|
221
|
+
validation emits no new proof or publication outputs. Recheck original inventory,
|
|
222
|
+
source bytes, integrity and path immediately before publication. The controls
|
|
223
|
+
assume trusted workflow code and runner, not a fully compromised runner.
|
|
224
|
+
|
|
225
|
+
Git commit identity, checkout byte inventory and archive digest are distinct.
|
|
226
|
+
CRLF/LF checkout conversion can produce different valid inventories on different
|
|
227
|
+
systems. Qualification binds the exact Windows-built asset to its recorded
|
|
228
|
+
checkout bytes; the nine OS/Node CI jobs qualify their own checkouts, not that
|
|
229
|
+
Windows archive on every OS. After the final commit, establish an inventory for
|
|
230
|
+
that commit and reinspect the same frozen bytes before release. Retain the
|
|
231
|
+
pre-commit evidence as historical evidence, not as the final commit binding.
|
|
232
|
+
On Windows, use native PowerShell paths, environment assignments and output
|
|
233
|
+
handling instead of the Bash syntax above.
|
|
234
|
+
|
|
235
|
+
## Separate qualification from publication
|
|
236
|
+
|
|
237
|
+
The npm qualification job has `contents: read`, no OIDC permission and no
|
|
238
|
+
secrets. It captures the source inventory before installation/tests/packing,
|
|
239
|
+
then installs, tests, audits and qualifies the exact archive and consumer.
|
|
240
|
+
The publication job starts on a fresh runner after qualification succeeds.
|
|
241
|
+
Both checkouts are pinned to the event's `github.sha`; HEAD and the actual tag
|
|
242
|
+
target must match that commit. Build outputs cannot select the privileged code.
|
|
243
|
+
|
|
244
|
+
Only the `.tgz`, pack JSON and source inventory cross the job boundary through
|
|
245
|
+
the exact upload artifact ID, downloaded outside the fresh checkout. No scripts,
|
|
246
|
+
`node_modules`, caches, environment files or first-runner proof are transferred.
|
|
247
|
+
The inventory is portable: commit, release identity, relative paths, sizes and
|
|
248
|
+
byte hashes. Each command validates its own explicit canonical source root;
|
|
249
|
+
moving roots never rewrites the original inventory bytes or SHA256.
|
|
250
|
+
|
|
251
|
+
Qualification job outputs retain the original inventory SHA256 and archive
|
|
252
|
+
SHA512. Fresh privileged `inspect` must enforce that original expected integrity
|
|
253
|
+
against both pack JSON and compressed bytes before creating a local proof or
|
|
254
|
+
outputs. It binds the portable inventory to the fresh source and newly downloaded
|
|
255
|
+
canonical archive path. The final `check` uses the original inventory digest and
|
|
256
|
+
the privileged inspection's integrity/path outputs; npm publishes only that
|
|
257
|
+
checked path with `--provenance` and lifecycle scripts disabled. Registry
|
|
258
|
+
verification retains those same original bindings.
|
|
259
|
+
|
|
260
|
+
The privileged inspection receives `SOURCE_INVENTORY_SHA256` and
|
|
261
|
+
`BUILD_INTEGRITY` from the original qualification job outputs:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
node scripts/release-artifact.mjs inspect "$RUNNER_TEMP/release-input/release-pack.json" --source-root "$GITHUB_WORKSPACE" --inventory "$RUNNER_TEMP/release-input/source-inventory.json" --inventory-sha256 "$SOURCE_INVENTORY_SHA256" --expected-integrity "$BUILD_INTEGRITY"
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The first qualification inspection emits the original archive digest; this
|
|
268
|
+
fresh privileged inspection must require it, not choose a new expected value.
|
|
269
|
+
|
|
270
|
+
Privileged jobs run only approved validators using Node built-ins, the npm CLI
|
|
271
|
+
or the pinned MCP publisher. They run no `npm ci`/`npm install`, project tests,
|
|
272
|
+
consumer smoke checks or `--runtime`. `NPM_TOKEN`, when needed for bootstrap,
|
|
273
|
+
exists only in the final npm publish step. The MCP workflow uses the same job
|
|
274
|
+
separation. Its nonprivileged job first captures the inventory, then `fetch-npm`
|
|
275
|
+
verifies registry identity and SHA512, requires the fixed official HTTPS URL,
|
|
276
|
+
downloads within the 8 MiB/time bounds and inspects all 89 files. It installs that
|
|
277
|
+
actual download without scripts, checks the graph and runs consumer checks. The
|
|
278
|
+
job output retains this qualified archive's original integrity. The privileged
|
|
279
|
+
job receives it and requires it from the registry again before authentication:
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
node scripts/release-artifact.mjs verify-npm --source-root "$GITHUB_WORKSPACE" --expected-integrity "$QUALIFIED_INTEGRITY" --distribution npm
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The standalone `verify-npm` form therefore enforces expected integrity too.
|
|
286
|
+
No dependency files or code from the first runner are transferred; only the fresh
|
|
287
|
+
event-pinned checkout supplies the published `server.json`.
|
|
288
|
+
|
|
289
|
+
## GitHub release and assets
|
|
290
|
+
|
|
291
|
+
After independent qualification and review, use the final verified commit,
|
|
292
|
+
require its complete public CI, then create v0.3.1. Retain the exact validated
|
|
293
|
+
`ftp-deploy-mcp-0.3.1-source.tar.gz` bytes. Prepare `SHA256SUMS` for this archive,
|
|
294
|
+
the standalone bilingual HTML guide and final evidence/SBOM; upload only these
|
|
295
|
+
verified files. Publish EN/FR notes that accurately describe the source scope,
|
|
296
|
+
then download again and verify hashes. Record commit, tag, actual time, names
|
|
297
|
+
and digests.
|
|
298
|
+
|
|
299
|
+
Attach no drifting npm `.tgz`. GitHub's automatic ZIP/tar remains the separate
|
|
300
|
+
complete repository. Asset uploads remain pending until verified; npm and MCP
|
|
301
|
+
remain unqualified/pending. A source release does not make npm, `npx` or the
|
|
302
|
+
Official MCP Registry available.
|
|
303
|
+
|
|
304
|
+
## Publish npm
|
|
305
|
+
|
|
306
|
+
1. Review the prepared release workflow and confirm its trigger matches the
|
|
307
|
+
intended tag policy.
|
|
308
|
+
2. Create an annotated tag whose name exactly matches the version:
|
|
309
|
+
`git tag -a v0.3.1 -m "v0.3.1"`. If the GitHub source release already created
|
|
310
|
+
this tag, verify and reuse its unchanged target; do not recreate it.
|
|
311
|
+
3. After maintainer approval and credential readiness, push the release commit
|
|
312
|
+
and tag. Ensure the workflow is present on the default branch, then dispatch
|
|
313
|
+
it explicitly on the tag:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
gh workflow run release.yml --ref v0.3.1
|
|
317
|
+
```
|
|
318
|
+
4. Require tests, version-consistency checks, installed graph equality, tarball inspection, and provenance
|
|
319
|
+
generation to pass before the publish step.
|
|
320
|
+
5. Wait for that workflow run to finish successfully. Its last step compares
|
|
321
|
+
the public npm name, exact version, `mcpName`, and SHA512 integrity to the
|
|
322
|
+
validated archive. Only 404 propagation responses are retried (six attempts,
|
|
323
|
+
five-second delay, 15-second request timeout); authentication, HTTP errors,
|
|
324
|
+
invalid metadata, and mismatched integrity fail closed. A failed check after
|
|
325
|
+
publish does **not** prove npm publication failed: inspect the exact version
|
|
326
|
+
before doing anything else, and do not rerun npm publication blindly.
|
|
327
|
+
6. For the first-publish bootstrap, configure Trusted Publishing and revoke and
|
|
328
|
+
remove the token immediately after success. For later releases, leave
|
|
329
|
+
`NPM_TOKEN` absent so npm authenticates with GitHub OIDC. Verify the trust
|
|
330
|
+
configuration before the next release; do not republish the same version to
|
|
331
|
+
test it.
|
|
332
|
+
7. Verify the public artifact independently:
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
npm view ftp-deploy-mcp@0.3.1 name version mcpName dist.integrity
|
|
336
|
+
npm view ftp-deploy-mcp@0.3.1 dist.tarball
|
|
337
|
+
npx -y ftp-deploy-mcp@0.3.1 --version
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Compare the registry integrity/version with the validated tarball and tag.
|
|
341
|
+
Only after these checks pass should the README call `npx` a currently available
|
|
342
|
+
installation method.
|
|
343
|
+
|
|
344
|
+
Create the GitHub release from the same immutable tag and copy the relevant
|
|
345
|
+
changelog entry. Do not move or reuse a published tag.
|
|
346
|
+
|
|
347
|
+
## Publish to the MCP registry
|
|
348
|
+
|
|
349
|
+
This step remains pending. Publish only after separate npm qualification, including the installed graph. The MCP workflow downloads and qualifies the actual published npm archive again; identity alone is insufficient:
|
|
350
|
+
|
|
351
|
+
1. Review `server.json` against schema `2025-12-11`; it must agree with the tag,
|
|
352
|
+
source lockfile root/root-package versions, runtime version, npm identifier,
|
|
353
|
+
and `mcpName`.
|
|
354
|
+
2. Dispatch the separate workflow on exactly the same tag:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
gh workflow run publish-mcp.yml --ref v0.3.1
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
3. The nonprivileged job also qualifies the actual npm download and installed graph. Its
|
|
361
|
+
fresh privileged successor verifies structural metadata, the exact npm
|
|
362
|
+
version, `mcpName` and the qualified integrity **before** MCP authentication, without dependency
|
|
363
|
+
installation, tests or `--runtime`.
|
|
364
|
+
4. It verifies the pinned official publisher archive, then runs
|
|
365
|
+
`mcp-publisher login github-oidc` and `mcp-publisher publish`. GitHub OIDC
|
|
366
|
+
proves the `io.github.alebgl77/` namespace; no dedicated MCP secret is used.
|
|
367
|
+
The publisher validates the manifest during publication. The workflow logs
|
|
368
|
+
out afterwards. See the [official GitHub Actions guide](https://modelcontextprotocol.io/registry/github-actions)
|
|
369
|
+
and [registry quickstart](https://modelcontextprotocol.io/registry/quickstart).
|
|
370
|
+
5. From a clean environment, find the official registry entry, install it by
|
|
371
|
+
its documented route, start the server, and confirm `--version` plus an MCP
|
|
372
|
+
`ftp_list_servers` call.
|
|
373
|
+
6. Check that Glama and MCP Index discovery listings point to the canonical
|
|
374
|
+
repository and released version, but do not treat those third-party pages as
|
|
375
|
+
registry verification.
|
|
376
|
+
|
|
377
|
+
The workflow pins `mcp-publisher` **v1.8.1**, Linux amd64, SHA256:
|
|
378
|
+
|
|
379
|
+
```text
|
|
380
|
+
a06c9096dcb9727c13555b6be26c7effa707b01f06a4c561ba7a3635443cf2cc
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
On 2026-09-03, the downloaded archive matched the digest returned by the
|
|
384
|
+
[official GitHub release API](https://api.github.com/repos/modelcontextprotocol/registry/releases/tags/v1.8.1)
|
|
385
|
+
for the [v1.8.1 release](https://github.com/modelcontextprotocol/registry/releases/tag/v1.8.1).
|
|
386
|
+
The workflow verifies this SHA256 before extracting or executing the binary;
|
|
387
|
+
it never resolves a mutable `latest` URL. This is a pinned digest verification,
|
|
388
|
+
not a claim that a Sigstore signature was independently verified. Updating the
|
|
389
|
+
tool requires reviewing and re-verifying both the version and digest.
|
|
390
|
+
|
|
391
|
+
If npm succeeded but MCP publication failed, correct the MCP-specific issue
|
|
392
|
+
and rerun only `publish-mcp.yml` on the unchanged tag. Do not attempt to recreate
|
|
393
|
+
an existing npm version. The MCP Registry is a preview service; check its live
|
|
394
|
+
entry after publishing before making availability claims.
|
|
395
|
+
|
|
396
|
+
## Rollback and incident response
|
|
397
|
+
|
|
398
|
+
Before publication, stop the workflow and fix the release commit. If an
|
|
399
|
+
unpublished tag was pushed, remove it only after confirming no artifact was
|
|
400
|
+
created, then create a corrected tag.
|
|
401
|
+
|
|
402
|
+
After npm publication, assume the version is immutable:
|
|
403
|
+
|
|
404
|
+
- stop MCP registry publication if it has not happened;
|
|
405
|
+
- deprecate the broken npm version with a precise warning;
|
|
406
|
+
- fix forward with a new patch version and a new tag;
|
|
407
|
+
- use npm unpublish only for a qualifying emergency and within npm policy, not
|
|
408
|
+
as a normal rollback;
|
|
409
|
+
- never overwrite, move, or reuse the published tag/version;
|
|
410
|
+
- withdraw or deprecate the MCP registry version if the registry supports it,
|
|
411
|
+
then publish the fixed patch version;
|
|
412
|
+
- revoke any bootstrap token, rotate exposed credentials, and preserve logs and
|
|
413
|
+
integrity values for incident review;
|
|
414
|
+
- revert README availability claims if users can no longer install a safe
|
|
415
|
+
version.
|
|
416
|
+
|
|
417
|
+
For a suspected security issue, pause the release and follow
|
|
418
|
+
[SECURITY.md](../SECURITY.md).
|
|
419
|
+
|
|
420
|
+
## Post-release checks
|
|
421
|
+
|
|
422
|
+
For the source release, verify its archive, checksum, source graph, runtime and
|
|
423
|
+
external commit inventory. Perform the following registry checks only after
|
|
424
|
+
separate npm/MCP publication has actually succeeded.
|
|
425
|
+
|
|
426
|
+
- Confirm npm provenance is visible and refers to the expected repository,
|
|
427
|
+
workflow, commit, and tag.
|
|
428
|
+
- Confirm source, tarball, GitHub release, npm, server `--version`, and MCP
|
|
429
|
+
registry all report the same version.
|
|
430
|
+
- Test source install, exact-version `npx`, and registry installation from
|
|
431
|
+
clean environments.
|
|
432
|
+
- Update both README availability notices and add the verified `npx` path.
|
|
433
|
+
- Announce only installation paths that were actually tested.
|
|
434
|
+
- Monitor private security reports and release failures, and prepare a patch
|
|
435
|
+
rather than altering the published artifact.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Limites du parcours local et de l’admission des appels
|
|
2
|
+
|
|
3
|
+
[English](./RESOURCE-BOUNDS.md)
|
|
4
|
+
|
|
5
|
+
## Sélection d’un déploiement
|
|
6
|
+
|
|
7
|
+
Chaque serveur configuré possède deux politiques facultatives, qui doivent être
|
|
8
|
+
des entiers positifs représentables sans perte. Les arguments des outils ne
|
|
9
|
+
peuvent modifier aucune de ces politiques.
|
|
10
|
+
|
|
11
|
+
| Champ | Défaut | Maximum |
|
|
12
|
+
| --- | ---: | ---: |
|
|
13
|
+
| `maxScanEntries` | 100000 entrées visitées | 1000000 |
|
|
14
|
+
| `maxScanDepth` | 64 niveaux de dossiers sous la racine | 256 |
|
|
15
|
+
|
|
16
|
+
La racine compte pour une entrée visitée, à la profondeur zéro. Chaque entrée
|
|
17
|
+
découverte compte une fois : dossiers, fichiers exclus et liens symboliques
|
|
18
|
+
compris. La descente ne compte pas le dossier une seconde fois. Atteindre
|
|
19
|
+
exactement une limite est accepté ; l’entrée suivante refuse toute la sélection
|
|
20
|
+
avec `SCAN_LIMIT`. La profondeur est inclusive : les fichiers d’un dossier au
|
|
21
|
+
niveau configuré restent éligibles, mais un sous-dossier non élagué est refusé
|
|
22
|
+
avant son ouverture. Un sous-arbre élagué ne nécessite aucune descente et ses
|
|
23
|
+
descendants non découverts ne sont pas comptés.
|
|
24
|
+
|
|
25
|
+
`maxDeployFiles` borne séparément les fichiers sélectionnés dès leur accumulation.
|
|
26
|
+
Les contrôles existants d’octets par fichier et par déploiement restent actifs.
|
|
27
|
+
Un échec de sélection précède toute connexion ou mutation distante, en simulation
|
|
28
|
+
comme en déploiement réel. `SCAN_LIMIT` renvoie `effects:none`, `retryable:false`
|
|
29
|
+
et `next_action:fix_input`.
|
|
30
|
+
|
|
31
|
+
Le parcours lit les dossiers séquentiellement avec `opendir` asynchrone, un tampon
|
|
32
|
+
de 32 entrées par handle et au plus `maxScanDepth + 1` handles ouverts. Il vérifie
|
|
33
|
+
l’annulation et le délai autour des lectures, puis rend la main à la boucle
|
|
34
|
+
événementielle toutes les 256 entrées découvertes. Toutes les fermetures sont
|
|
35
|
+
attendues, même après échec ou annulation. Un appel au système de fichiers ou une
|
|
36
|
+
fermeture lente peut encore dépasser le délai réel ; une réponse anticipée de
|
|
37
|
+
dépassement de délai ne signifie pas que le nettoyage est terminé.
|
|
38
|
+
|
|
39
|
+
Seules les exclusions intégrées prouvées des sous-arbres `node_modules`, `.git`
|
|
40
|
+
et `.ftp-mcp` permettent l’élagage. Les exclusions personnalisées et les motifs
|
|
41
|
+
d’inclusion ne l’autorisent jamais ; leurs règles existantes restent appliquées
|
|
42
|
+
à chaque fichier. Cela corrige un ancien test par sentinelle qui produisait des
|
|
43
|
+
faux positifs : `exclude:["**/__ftp_deploy_probe__"]` ne masque plus
|
|
44
|
+
`docs/wanted.txt`. Certains fichiers omis à tort peuvent désormais apparaître
|
|
45
|
+
dans la sélection. Examinez une simulation après avoir changé les exclusions.
|
|
46
|
+
|
|
47
|
+
Les liens symboliques rencontrés sont ignorés. Les contrôles lexicaux et
|
|
48
|
+
canoniques de `localRoot` et la nouvelle validation des sources à l’envoi restent
|
|
49
|
+
actifs. Ils ne suppriment pas les courses malveillantes sur les chemins causées
|
|
50
|
+
par un autre programme exécuté sous le même compte système.
|
|
51
|
+
|
|
52
|
+
## Admission des appels d’outil
|
|
53
|
+
|
|
54
|
+
Un isolate Node admet au plus 64 appels d’outil pour tous les registres partageant
|
|
55
|
+
le module d’admission. Il n’existe ni file d’admission supplémentaire ni reprise
|
|
56
|
+
automatique. Un outil inconnu conserve l’erreur de protocole `-32602` ; des
|
|
57
|
+
arguments invalides conservent `INVALID_ARGUMENT`. Une requête déjà annulée ne
|
|
58
|
+
lance aucune préparation ni aucun handler. L’admission suit la validation du
|
|
59
|
+
schéma et précède la préparation.
|
|
60
|
+
|
|
61
|
+
Le 65e appel renvoie `CAPACITY_LIMIT`, `effects:none`, `retryable:false` et
|
|
62
|
+
`next_action:retry`. Réessayez explicitement après la fin effective d’un appel
|
|
63
|
+
actif. Chaque appel conserve sa place pendant la préparation, l’exécution et le
|
|
64
|
+
nettoyage, y compris lorsqu’une entrée-sortie non coopérative se termine après
|
|
65
|
+
une annulation ou un dépassement de délai. Les verrous FIFO existants par cible
|
|
66
|
+
sont inchangés ; leurs appels en attente occupent aussi une place d’admission.
|
|
67
|
+
Les identifiants de corrélation, dont `0` numérique, `""`, `1` numérique et `"0"`
|
|
68
|
+
texte, restent distincts dans la garde du canal possédée par le point d’entrée.
|
|
69
|
+
Cette garde réserve chaque identifiant de requête entrant, toutes méthodes
|
|
70
|
+
confondues, avant sa remise au SDK et ferme la connexion sur tout doublon actif.
|
|
71
|
+
|
|
72
|
+
Ces limites ne coordonnent pas les processus, isolates ou hôtes distincts. Elles
|
|
73
|
+
ne bornent ni les tampons d’entrée du transport ni l’analyse des requêtes refusées.
|
|
74
|
+
Les limites du parcours local ne bornent pas le tampon distant sous-jacent de
|
|
75
|
+
`list()` ; la sortie MCP paginée conserve l’implémentation de liste existante.
|
|
76
|
+
|
|
77
|
+
## Bornes de l'état durable et du statut
|
|
78
|
+
|
|
79
|
+
`stateLimits` borne strictement les octets d'état, octets/fichiers de sauvegarde,
|
|
80
|
+
plans, opérations, fichiers/octets de plan et octets/événements de journal. La
|
|
81
|
+
publication d'un plan prouve toute sa réservation avant écriture. Apply et
|
|
82
|
+
rollback consultent le budget avant chaque intention durable et événement
|
|
83
|
+
terminal ; les flux s'exécutent hors du court verrou d'admission mais sous le
|
|
84
|
+
verrou persistant de l'alias exact.
|
|
85
|
+
|
|
86
|
+
`ftp_deploy_status` est sans écriture ni verrou : il rejoue un préfixe de journal
|
|
87
|
+
dont l'intégrité est vérifiée, puis confirme que plan, association, journal et
|
|
88
|
+
tête n'ont pas changé. Une concurrence renvoie `STATUS_CHANGED` ou
|
|
89
|
+
`DURABLE_STATE_BUSY`, jamais une page mélangée. Une page accepte au plus 100
|
|
90
|
+
lignes et s'arrête avant que le résultat structuré complet dépasse 18 Kio ;
|
|
91
|
+
`next_offset` vaut toujours `offset + count` lorsqu'une ligne reste.
|
|
92
|
+
|
|
93
|
+
Sauvegardes et journaux sont sensibles. Gardez `stateDir` privé et hors de toute
|
|
94
|
+
arborescence `localRoot`. Ne copiez pas un état v1 : il est refusé avant décodage
|
|
95
|
+
des plans et exige une réinitialisation hors ligne.
|
|
96
|
+
|
|
97
|
+
## Cycle de vie interne des outils préparés
|
|
98
|
+
|
|
99
|
+
Le registre expose une interface d’intégration interne ; les outils qui ne
|
|
100
|
+
l’activent pas utilisent toujours leur résolveur `timeoutFor` ordinaire.
|
|
101
|
+
L’interface elle-même n’active aucune reprise ni identifiant de requête
|
|
102
|
+
persistant et ne modifie ni les descripteurs ni les résultats des outils qui ne
|
|
103
|
+
l’utilisent pas.
|
|
104
|
+
|
|
105
|
+
Une inscription peut fournir les callbacks privés appariés `prepare(args,
|
|
106
|
+
operation)` et `disposePrepared(prepared, operation)`, tous deux obligatoirement
|
|
107
|
+
des fonctions. Le handler reçoit la valeur préparée en troisième argument. Une
|
|
108
|
+
préparation réussie transfère la propriété de sa ressource même si elle renvoie
|
|
109
|
+
`undefined` ; le registre attend exactement une libération après le handler ou
|
|
110
|
+
tout échec ultérieur. Une préparation qui rejette doit fermer chaque ressource
|
|
111
|
+
acquise avant son rejet : le registre ne peut pas libérer un contexte jamais
|
|
112
|
+
renvoyé. Le premier échec est conservé si la libération échoue aussi ; un échec
|
|
113
|
+
de libération après un handler réussi devient une erreur d’outil bornée ordinaire.
|
|
114
|
+
|
|
115
|
+
Préparation, traitement et libération s’exécutent dans le worker admis observé
|
|
116
|
+
par le transport. Une annulation ou un dépassement de délai peut produire une
|
|
117
|
+
réponse anticipée, mais le slot d’admission reste occupé jusqu’à la terminaison
|
|
118
|
+
réelle, préparation tardive et libération comprises. L’identifiant de requête du
|
|
119
|
+
canal reste réservé jusqu’au règlement ou à l’échec de l’envoi de la réponse
|
|
120
|
+
terminale et jusqu’à la terminaison réelle de tous les tours de handler enregistrés,
|
|
121
|
+
y compris tous les tours legacy d’entrée requise. Si l’annulation du client
|
|
122
|
+
supprime la réponse, l’identifiant reste réservé jusqu’à la fermeture de la
|
|
123
|
+
connexion. La libération s’exécute même après l’annulation et ne doit pas passer
|
|
124
|
+
par `operation.step`, dont le contrôle d’annulation empêcherait le nettoyage. Un
|
|
125
|
+
travail non coopératif peut donc retenir la capacité indéfiniment ; le délai
|
|
126
|
+
n’interrompt pas du JavaScript ou des entrées-sorties arbitraires.
|
|
127
|
+
|
|
128
|
+
L’option privée `preparedTimeoutMs` du registre vaut 120000 par défaut et accepte
|
|
129
|
+
un entier sûr positif jusqu’à 3600000. Les outils préparés ne consultent jamais
|
|
130
|
+
`timeoutFor`. Le futur assemblage fournit le plus grand délai serveur validé.
|
|
131
|
+
Après sélection du serveur, la préparation appelle
|
|
132
|
+
`operation.shortenTimeout(server.operationTimeoutMs)`, puis `operation.check()`
|
|
133
|
+
avant tout effet. Le raccourcissement part du début initial de l’opération, ne
|
|
134
|
+
prolonge jamais l’échéance courante et annule immédiatement une échéance déjà
|
|
135
|
+
dépassée. Une opération terminée ne peut pas créer un autre timer.
|
|
136
|
+
|
|
137
|
+
`operation.runPreparation(run)` est à usage unique, sans imbrication. Son plafond
|
|
138
|
+
fixe de 10000 ms commence avant l’appel du callback et reste borné par l’échéance
|
|
139
|
+
principale. Les contrôles avant et après le callback imposent aussi ce plafond
|
|
140
|
+
si la boucle événementielle n’a pas exécuté le timer. Son expiration annule
|
|
141
|
+
irréversiblement toute l’opération avec `TIMEOUT`. Une préparation réussie retire
|
|
142
|
+
seulement le plafond de l’étape et conserve l’échéance principale. Le callback
|
|
143
|
+
réel reste attendu après une réponse anticipée ; les timers sont supprimés à
|
|
144
|
+
l’annulation et à la terminaison réelle.
|