Skip to content

chore(deps): dependency audit — 9 packages, rmcp protocol-negotiation flip, tree-sitter 0.27 deferred - #729

Merged
coseto6125 merged 2 commits into
mainfrom
chore/dep-audit-2026-09-01
Sep 1, 2026
Merged

coseto6125 merged 2 commits into
mainfrom
chore/dep-audit-2026-09-01

Conversation

@coseto6125

Copy link
Copy Markdown
Owner

Full dependency audit across the repo's one ecosystem with dependencies (Rust; the root pyproject.toml is ruff config, and packaging/npm + packaging/pypi are binary shims with no third-party deps and no lockfile).

Upgrades applied

Lockfile only — no manifest change was needed for any of them.

crate from to direct?
rmcp / rmcp-macros 3.1.4 3.2.0 direct (ecp-mcp)
uuid 1.25.0 1.26.0 direct (ecp-cli) — same bump as #725, already on main
borsh / borsh-derive 1.8.0 1.8.1 transitive
cpufeatures 0.3.0 0.3.1 transitive
indexmap 2.14.0 2.14.1 transitive
lru 0.18.2 0.18.3 transitive (ratatui-core's copy)
tree-sitter-language 0.1.7 0.1.8 transitive

5 of 73 direct dependencies were behind. Three moved; two stay, below.

Flagged risks

cc 1.2.67 → 1.4.4 does not resolve. tree-sitter-sequel 0.3.11 declares a build-dep on cc ~1.2.1, which caps the graph at 1.2.x. Every other grammar (tree-sitter ^1.2.48, solidity/verilog/bash ^1.1, hcl/lua/containerfile/yaml ^1.2) uses a caret range that would accept 1.4. Revisit when tree-sitter-sequel relaxes the tilde.

tree-sitter 0.26.13 → 0.27.0 and ast-grep-core 0.45.2 → 0.45.3 are one change, not two. ast-grep-core 0.45.3 requires tree-sitter ^0.27.0 ("chore: update tree-sitter 0.27"), and tree-sitter carries links = "tree-sitter", so cargo permits exactly one copy in the graph — the workspace moves together or not at all.

The move is feasible. I probed it: the only blockers are our own seven vendored grammars pinning 0.26 (crates/vendor/tree-sitter-{astro,cairo,nim,svelte,swift,vue,vyper}, six as dev-deps, nim as a real dependency), and with those relaxed cargo update -p ast-grep-core --precise 0.45.3 resolves cleanly to tree-sitter 0.27.0 + tree-sitter-language 0.1.8. tree-sitter-md 0.5.3 is not a blocker despite declaring ^0.26 — that dep is a dev-dep, and the lock resolves it to cc + tree-sitter-language only.

It is not in this PR because 0.27 (published 2026-08-30) carries two changes that reach us:

  • fix(rust)!: return u32 from Node::child_count (tree-sitter#5313) — 19 call sites in crates/, several of which already write as u32 and would then trip clippy's unnecessary_cast under -D warnings.
  • fix(query): prevent cross-branch capture contamination in alternations with quantifiers (tree-sitter#5317) — a query-engine correctness fix that can move parse output.

Per CLAUDE.md that is a parser/core change and needs the 14-language matrix. Own PR. Filed as a follow-up with this evidence.

Advisories

osv-scanner scan source --lockfile Cargo.lock before and after: one advisory, unchanged.

RUSTSEC-2026-0253 — lru 0.16.4, use-after-free from a non-panic-safe LruCache::pop(). Reached only through tantivy 0.26.1, which requires lru ^0.16.3 and is the latest tantivy (verified against crates.io today), so this repo cannot reach the fix (lru ≥ 0.18.2) from its own manifests. The other lru in the lock, 0.18.3 via ratatui-core, is already patched. The reason, the unreachability argument and the pinning test are recorded in .cargo/audit.toml and crates/ecp-cli/tests/supply_chain_pins.rs; nothing about them changed.

Behavior flip: rmcp 3.2.0 protocol negotiation

The one flip in this batch, and it lands on a branch we own.

list_tools_result (crates/ecp-mcp/src/server.rs) gates the SEP-2549 ttlMs / cacheScope fields on context.protocol_version() >= V_2026_07_28. rmcp 3.2.0 changes what that returns.

Before, negotiate_protocol_version echoed the client's requested version whenever supported_protocol_versions() contained it, so a client handshaking as 2026-07-28 got it back and we attached the hints. rust-sdk#1228 fixed that against the 2026-07-28 versioning spec: an initialize request selects legacy semantics whatever version it names.

Reading rmcp-3.2.0/src/service/server.rs, every path through the new negotiate_protocol_version returns a legacy version or an error, and uses_discover_lifecycle is set only in the streamable-HTTP tower service. serve_stdio goes through serve_server, so on stdio the hints branch is now unreachable.

Decision: adopt. Sending 2026-07-28-only fields to a peer the spec calls legacy is the wrong behavior, and rmcp removed the knob that would let us do it anyway. The branch stays — it is what a discover-lifecycle peer must receive, its three unit tests still pin the mapping, and it goes live the day ecp gains an HTTP transport or rmcp gives stdio that lifecycle. The second commit records that next to the code, so the branch does not read as deletable dead code, and moves the requirement to 3.2 so the note cannot go stale against a lock that resolved 3.1.x.

The other 3.2.0 changes do not reach us: the OAuth credential-store and streamable-HTTP concurrency work is behind features we do not enable (server + transport-io only). uuid 1.26.0 adds ContextV7::with_additional_precision_bits and nothing else; our whole surface is one use uuid::Uuid.

Gates

Run per crate at -j 4, serially, per this repo's build-concurrency rules.

gate result
cargo check --all-targets × 4 crates clean
cargo clippy --all-targets --all-features -- -D warnings × 4 crates clean
cargo fmt --all -- --check clean
cargo test -p ecp-mcp --tests 50 passed
cargo test -p ecp-core --tests 366 passed
cargo test -p ecp-analyzer --tests 403 passed
cargo test -p egent-code-plexus --tests 58 targets, 634 passed, 1 failed, 4 ignored

The one failure is pre-existing, and proving that turned up a real bug.

find_cmd::find_exact_match_returns_single_definition asserts a fixture node at src/auto_ensure.rs; it gets crates/ecp-cli/src/auto_ensure.rs, this repo's own ensure_index.

  • Not from this change: the same suite on origin/main's own Cargo.lock, same machine, gives the identical 58 / 634 / 1 / 4.
  • Root cause: ECP_HOME. The test passes --graph <tmpdir>/graph.bin, but ecp find still merges the ambient graph from the developer's real ~/.ecp, and the ambient ensure_index (23 callers) outranks the fixture's (0), so top-1 returns the wrong one. With ECP_HOME pointed at an empty directory all 16 find_cmd tests pass.
  • CI is green because CI's ~/.ecp never holds this repo, which is what makes it worth a separate issue: --graph does not isolate a caller from the ambient overlay, and the only place that shows up is a maintainer's machine. Filed as a follow-up; out of scope here.

Not run

Step 6, the alternatives scan (KEEP / EVALUATE per core package), runs only when replacements are asked about. They were not.

Lockfile-only refresh. Nothing here needed adaptation code; every crate
builds, clippies and tests unchanged.

    borsh / borsh-derive   1.8.0  -> 1.8.1
    cpufeatures            0.3.0  -> 0.3.1
    indexmap               2.14.0 -> 2.14.1
    lru                    0.18.2 -> 0.18.3   (ratatui-core's copy)
    rmcp / rmcp-macros     3.1.4  -> 3.2.0
    tree-sitter-language   0.1.7  -> 0.1.8
    uuid                   1.25.0 -> 1.26.0

Five direct dependencies were behind. Two stay behind, for reasons that
are not ours to fix from this repo:

- `cc` 1.2.67 -> 1.4.4 does not resolve. tree-sitter-sequel 0.3.11
  declares a build-dep on `cc ~1.2.1`, which caps the whole graph at
  1.2.x; every other grammar uses a caret range that would accept 1.4.
- `tree-sitter` 0.26.13 -> 0.27.0 and `ast-grep-core` 0.45.2 -> 0.45.3
  are one change, not two: ast-grep-core 0.45.3 requires tree-sitter
  ^0.27.0, and `tree-sitter` carries `links = "tree-sitter"`, so exactly
  one copy may exist in the graph. The move is feasible — the only
  blockers are our own seven vendored grammars pinning 0.26, and with
  those relaxed the resolve succeeds — but 0.27 returns `u32` from
  `Node::child_count` (tree-sitter#5313, 19 call sites here) and fixes
  cross-branch capture contamination in query alternations
  (tree-sitter#5317), which can move parse output. That belongs in its
  own PR with the 14-language matrix, not bundled into a lock refresh.

RUSTSEC-2026-0253 (lru 0.16.4, via tantivy 0.26.1's `lru ^0.16.3`)
remains the one open advisory, still with the reason and the pinning
test recorded in .cargo/audit.toml. tantivy 0.26.1 is the latest.

Gates, run per crate at -j 4:

    check     ecp-core, ecp-analyzer, ecp-mcp, egent-code-plexus   clean
    clippy    same four, --all-targets --all-features -D warnings  clean
    fmt       cargo fmt --all --check                              clean
    test      ecp-mcp 50, ecp-core 366, ecp-analyzer 403           pass
    test      egent-code-plexus  58 targets, 634 passed, 1 failed

That one failure, find_cmd::find_exact_match_returns_single_definition,
is pre-existing and not caused by this change: the same suite on
origin/main's own Cargo.lock gives the identical 58 / 634 / 1 / 4. It
depends on the developer's real ~/.ecp — with ECP_HOME pointed at an
empty directory all 16 find_cmd tests pass. CI is green because CI's
~/.ecp never holds this repo.
rmcp 3.2.0 changes what `context.protocol_version()` returns, which is
the value `list_tools_result` gates the SEP-2549 `ttlMs` / `cacheScope`
fields on.

Before, `negotiate_protocol_version` echoed the client's requested
version whenever `supported_protocol_versions()` contained it, so a
client that handshook claiming `2026-07-28` got `2026-07-28` back and we
attached the hints. rust-sdk#1228 fixed that against the 2026-07-28
versioning spec: an `initialize` request selects legacy semantics
whatever version it names. Every path through the new
`negotiate_protocol_version` returns a legacy version or an error —
verified by reading rmcp-3.2.0/src/service/server.rs — and
`uses_discover_lifecycle` is set only in the streamable-HTTP tower
service. `serve_stdio` goes through `serve_server`, so on the stdio
transport the negotiated version is now always below the gate.

We adopt that. The alternative is sending 2026-07-28-only fields to a
peer the spec says is on legacy semantics, and rmcp removed the knob
that would let us anyway.

The branch stays. It is what a discover-lifecycle peer must receive, its
three unit tests still pin the mapping, and it goes live the day `ecp`
gains an HTTP transport or rmcp gives stdio that lifecycle. Without this
note the next reader has a branch with no reachable caller and a
plausible reason to delete it.

The dependency requirement moves 3.1 -> 3.2 so the comment cannot go
stale against a lockfile that resolved 3.1.x. No lockfile change: the
refresh in the parent commit already landed 3.2.0.
@coseto6125
coseto6125 enabled auto-merge (squash) September 1, 2026 02:16
@coseto6125 coseto6125 added the merge-queue Opt-in to Mergify merge queue label Sep 1, 2026
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor
ecp impact cache (0 symbols) — internal, used by ecp dev pr-analyze

[]

@github-actions github-actions Bot added the ecp:risk-low ecp signal label Sep 1, 2026
@coseto6125
coseto6125 merged commit 523d6a8 into main Sep 1, 2026
19 checks passed
@coseto6125
coseto6125 deleted the chore/dep-audit-2026-09-01 branch September 1, 2026 02:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ecp:risk-low ecp signal merge-queue Opt-in to Mergify merge queue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant