Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Fast truth

- Root server: `src/main.rs` selects stdio vs HTTP transport, parses CLI limits (`--profiles-path`, `--capture-dir` + capture quotas included), and mounts HTTP at `/mcp`.
- HTTP mounting uses `src/http_transport.rs::router` in production and the test harness. With rmcp 3.5, an exact discovery/stateless policy-row header disables legacy session mode on a per-request service clone, preserving modern initialize HTTP 404 / -32601 with no session allocation. Other headers keep SDK routing; SDK validation remains intact. `ServerConfig` / `ClientConfig` replace deprecated aliases; initialized test clients explicitly advertise `2025-11-25`, never SDK `LATEST` (now modern).
- MCP surface lives in `src/server.rs`; tool handlers are split under `src/tools/`, prompts under `src/prompts/`, resources under `src/resources/`.
- MCP version policy (`src/mcp_protocol.rs`): the product-owned `SUPPORTED_PROTOCOLS` table is the SINGLE source for advertised versions, lifecycle admission, capability views, and cache shaping. Exactly two rows — `2026-07-28` (preferred, `DiscoverStateless`, `ImmediatePrivate` cache, subscriptions on) and permanent `2025-11-25` (`InitializeSession`, `Omit` cache, subscriptions off). Lookup is exact-match only (`policy_for`); `cache_fields_for(Option<ProtocolVersion>)` grants fields ONLY to rows with `CachePolicy::ImmediatePrivate` — never by date/range, and unknown/future versions inherit no policy. `supported_protocol_versions()` / preferred policy derive from the table, never from `ProtocolVersion::KNOWN_VERSIONS`.
- Dual MCP lifecycle (`src/server.rs`): preferred modern `2026-07-28` discovery/stateless requests (`server/discover` + self-contained per-request `_meta`) and compatible legacy `2025-11-25` initialize/session requests. `get_info()` serves the MODERN view (rmcp intersects `subscriptions/listen` filters against `get_info().capabilities`), `initialize()` returns the legacy view with subscription disabled, `discover()` the modern view with `resources.subscribe: true`. Common surface: tools/resources/prompts/completions (no logging, list-change, or tasks). `accepted_subscription_filter`/`listen` are backed by one process-wide `ResourceEventHub` (`src/resource_events.rs`, capacity 256) shared by every stdio/HTTP handler and the port hotplug watcher; legacy `subscriptions/listen` stays `-32601`.
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## Unreleased

- Upgrade rmcp to 3.5, migrate deprecated configuration aliases, and preserve
policy-owned stateless HTTP rejection without weakening legacy compatibility.
- Patch MCP validation-tool transitive dependencies, retaining exact Inspector,
conformance, and historical rmcp client pins.
- Update rustls to 0.23.45 to reject TLS 1.3 handshake messages crossing
encryption-level boundaries (GHSA-2mjx-qc3c-rqvc).

| Version | Date | Highlights |
|---|---|---|
| [0.9.3](#093) | 2026-08-15 | `list_ports` schema fix: optional `PortInfo` USB fields (`vid`/`pid`/`interface`) no longer required in the tool output schema (schemars 1.2.2 `schema_with` + `skip_serializing_if` mismatch), same fix + regression guard for `DiagnosePortArgs.baud_rate`; schema-drift fixes (isolated/transitive ref resolution, stateless HTTP protocol metadata required); read-loop decomposition into named control-flow units + `ReadDriver` state machine, async-trait `ConnectionOpener`; CI hardening (immutable-SHA action pins, versioned nrfutil provisioning with checksum verification, locked MCP validation npm deps); docs cleanup; rmcp 3.1.0, actions/setup-node 7 |
Expand Down
40 changes: 20 additions & 20 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ categories = ["development-tools", "embedded"]

[dependencies]
async-trait = "0.1"
rmcp = { version = "3.0", features = ["server", "macros", "transport-io", "transport-streamable-http-server", "schemars"] }
rmcp = { version = "3.5", features = ["server", "macros", "transport-io", "transport-streamable-http-server", "schemars"] }
serialport = { version = "4.5", features = ["usbportinfo-interface"] }
tokio = { version = "1.41", features = ["macros", "rt-multi-thread", "sync", "time", "io-util", "signal", "net"] }
tokio-serial = "5.4"
Expand Down Expand Up @@ -48,7 +48,7 @@ name = "serial_mcp"
path = "src/lib.rs"

[dev-dependencies]
rmcp = { version = "3.0", features = ["client", "transport-streamable-http-client", "transport-streamable-http-client-reqwest", "transport-child-process"] }
rmcp = { version = "3.5", features = ["client", "transport-streamable-http-client", "transport-streamable-http-client-reqwest", "transport-child-process"] }
tokio = { version = "1.41", features = ["process", "fs"] }
anyhow = "1.0"
proptest = "1.6"
Expand Down
69 changes: 67 additions & 2 deletions compat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@ This directory contains two different kinds of compatibility assets. They have
different dependency-update rules and must not be treated as one dependency
tree.

## Security-alert triage rule

Prefer a patched dependency when it preserves the test's purpose. Dismiss an
alert as **Vulnerable code is not used** only after checking the advisory's
required feature, code path, input, and deployment conditions against the
actual build and runner. Record that evidence and what change would invalidate
it. A dependency's location under `compat/`, a development-only label, or a
passing test suite does not prove that vulnerable code is unreachable.

Keep the historical fixture's exact rmcp pin. Its purpose is to test the old
SDK, so upgrading rmcp removes the proof rather than fixing it. Other fixture
dependencies may receive compatible security patches if rmcp's exact version,
checksum, and enabled features remain unchanged and the full compatibility
gate passes. Their versions and behavior can still affect the proof; do not
refresh the whole historical lockfile without reviewing those changes.

For current validation tooling, keep the two direct MCP-tool pins unless their
upgrade is deliberate. Patch vulnerable transitives, review the lockfile diff,
install with `npm ci --ignore-scripts`, run npm audit, and run the full
compatibility gate. Patch and minor updates can still break behavior. Tests
reduce that risk; they do not guarantee that every dependency update is safe.

If a vulnerable path runs or its reachability is uncertain, leave the alert
open and fix or investigate it. Use **Tolerable risk** only for an explicit
owner-approved risk acceptance, not as a substitute for an available patch.

## Directory roles

### `mcp-validation/`
Expand Down Expand Up @@ -75,8 +101,8 @@ These updates did not weaken the historical-client guarantee:
## September 2026 rmcp alert assessment

Three Dependabot alerts point only to
`compat/rmcp-1-client/Cargo.lock`. Production serial-mcp resolves rmcp 3.1.4,
which is newer than every first patched version below.
`compat/rmcp-1-client/Cargo.lock`. At the September assessment, production
serial-mcp resolved rmcp 3.1.4, newer than every first patched version below.

| Advisory | Severity | First patched | Fixture assessment |
|---|---:|---:|---|
Expand All @@ -96,6 +122,45 @@ Reassess immediately if the historical fixture gains any of these behaviors:
- connections to untrusted remote endpoints instead of the bounded local
compatibility server.

## October 2026 security review

The October 10 review retained Dependabot dismissals #12–14 with the individual
feature/path reasons above. Alerts #15 (`fast-uri`, GHSA-58mr-gqgx-xq4g) and #16
(`undici`, GHSA-3wwx-pv8p-q78v) concern paths not exercised by the current
validation gate: there is no fast-uri-based host authorization followed by
fetching the original URL, and no WebSocket permessage-deflate client. These
packages are used; their vulnerable paths, not the packages themselves, are
outside the current gate. All five dismissals now carry explanatory comments.

Unlike the historical rmcp fixture, these npm transitives need not stay frozen.
The security patch set updates both undici branches, ip-address, fast-uri,
proxy-addr, source-map-js, and the conformance tool's transitive TypeScript SDK:

| Package | Patched lockfile version |
|---|---|
| Inspector's undici | 8.11.2 |
| Conformance's undici | 7.30.0 |
| ip-address | 10.7.3 |
| fast-uri override | 3.1.8 |
| proxy-addr | 2.0.8 |
| source-map-js | 1.2.2 |
| `@modelcontextprotocol/sdk` | 1.32.1 |

`npm audit --package-lock-only --ignore-scripts` reports zero vulnerabilities
for this patch set. The full shared compatibility runner passes with the
unchanged direct conformance/Inspector pins and historical rmcp fixture,
including the existing four expected fixture-dependent conformance failures.
This is point-in-time evidence, not a guarantee against future advisories.
Open GitHub alerts remain open until fixes reach the default branch; they are
not dismissed merely because the dependencies are validation tooling.

The refreshed alert list also includes historical rmcp OAuth SSRF
([GHSA-c9xm-49cp-xcr9](https://github.com/advisories/GHSA-c9xm-49cp-xcr9),
alert #29). Its metadata-fetch path lives in the excluded `auth` feature,
so it is dismissed with that evidence rather than changing the historical pin.
Root-lockfile alert #30 (GHSA-2mjx-qc3c-rqvc) is addressed by upgrading rustls
to 0.23.45; it does not require any historical-fixture change.

## Retaining `2025-11-25` compatibility

There are two separate decisions:
Expand Down
46 changes: 25 additions & 21 deletions compat/mcp-validation/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion compat/mcp-validation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"ink-form": {
"ink-select-input": "6.2.0"
},
"fast-uri": "3.1.6",
"fast-uri": "3.1.8",
"qs": "6.16.0"
}
}
3 changes: 3 additions & 0 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ supersede rather than rewrite accepted records.

Each record contains status, context and forces, considered options, decision,
rationale, consequences, and any superseding link.

- [Preserve policy-owned HTTP lifecycle routing](preserve-policy-owned-http-lifecycle-routing.md)
is proposed. The rmcp 3.5 migration preserves stateless initialize rejection.
Loading
Loading