Repository navigation
feat: MCP 2026-07-28 alongside 2025-11-25, SEP-2663 tasks, rmcp 3.6.0 (0.9.0) - #19
Merged
Merged
Conversation
…ged `OK`. A new `release_after_mutation` replaces it at all six post-mutation sites. It returns the session to the pool only if the connection answers, and never fails the call, because the mutation's own reply is the result. A server dropping the client right after an EXPUNGE no longer turns a finished delete into an error. - **Correction to my own finding:** `update_flags` never confirmed its STOREs, so there was no "completed flag change" to misreport. Its real bugs were in reading the result back. A dropped connection came out as "message not found", and it could return another message's flags when another client's update arrived first. Both are fixed. - **#18, short LIST:** a NOOP check now follows LIST, so a list cut short by a dropped connection is never returned or cached. - **#19, reads trusting a dead connection:** this was wider than reported. Besides misleading, "message not found" from a dropped connection made the subscription move, unsubscribe and ranking samples prune the ranking cache. The cache forgot messages that still existed, breaking the samples' own documented rule that "an outage must not masquerade as deletion". - **The fix:** `with_session_retry` now probes after the read, so `search_messages`, `get_messages`, `list_identities` and the other wrapped reads retry once on a fresh connection instead of returning a partial answer. A message now counts as missing only when a live server leaves it out. I added nine tests; eight failed before the fix for the reason they target, and one is a control. The existing sample-subject test had to answer the new NOOP. The cost is one extra NOOP round trip per retried read, and per fetch that comes back missing a message.
Agent Muse links this crate and is consolidating on one rustls crypto provider, aws-lc-rs: ring has no post-quantum key exchange, and two providers mean rustls can't pick one on its own. mail-auth's `aws-lc-rs` feature also selects hickory's TLS and DNSSEC backends, so ring leaves the build entirely. Lockfile: untrusted 0.7.1 added (aws-lc-rs), nothing else moved.
native-tls meant three TLS stacks for one connection: Secure Transport on macOS (deprecated since 10.15, and it never offered TLS 1.3, so IMAP topped out at 1.2), SChannel on Windows, OpenSSL on Linux, where it also needed libssl-dev to build. Agent Muse links this crate and ships one rustls provider, aws-lc-rs. IMAP now connects through tokio-rustls on aws-lc-rs (its `rustls` re-export is the one used, so the two can't drift), and rustls-platform-verifier hands the certificate check to the OS: Security.framework, CryptoAPI, or the system roots on Linux. Roots a person or their MDM installed still count. The provider is named, not taken from the process default, so agentmail behaves the same whether or not its host installed one. The configuration is built once per process (tls_client_config, now public so examples/probe_xoauth2.rs probes the same connection). What a server loses: TLS 1.2 suites without forward secrecy or in CBC mode; native-tls already refused anything below 1.2. What a host loses: AgentmailError::Tls now carries the handshake's std::io::Error (tokio- rustls's report, rustls's error inside) and is no longer a From conversion, which is breaking. It still counts as a connection error, so a failed handshake is retried once on a fresh connection as before. Tests: the configuration builds with the platform verifier, and an ignored live test completes a verified handshake with imap.gmail.com:993 and reads the IMAP greeting. Run on macOS, it negotiated TLS 1.3. rust-version moves to 1.99, the toolchain Agent Muse builds with. Co-Authored-By: Claude <noreply@anthropic.com>
dbus-secret-service-keyring-store reached Secret Service through the C libdbus (libdbus-sys), the last system library agentmail linked on Linux after the move to rustls: every Linux build needed libdbus-1-dev and pkg-config, in this repo's CI, release, security and nightly-stress workflows and in Agent Muse's Linux CI leg. zbus-secret-service-keyring-store is the same store over zbus, a D-Bus implementation in Rust: the same Store::new() -> Result<Arc<Store>>, and identical code for the item's search attributes (service, username, target), so a password saved by the old store is found by the new one. Features rt-async-io-crypto-rust: zbus runs its own async-io executor, so the store's blocking calls work from any thread, a tokio worker included, rather than needing a tokio context (rt-tokio); crypto stays pure Rust. On Linux the graph now has no libdbus-sys and no openssl-sys; what -sys crates remain build from bundled sources (aws-lc-sys, whose build script only optionally asks pkg-config, and libsqlite3-sys). The workflows stop installing libdbus-1-dev and pkg-config: the reusable CI and release workflows install their *-linux-packages only when given, and security and nightly-stress drop their apt step. Checked on macOS: fmt, clippy (all targets, -D warnings) and the tests. The Linux arm of main.rs is not compiled here (no Linux C toolchain on this machine); its one change is the store's crate name, whose constructor and trait impl match. Co-Authored-By: Claude <noreply@anthropic.com>
aws-lc-sys before 0.46 emits bindgen's `#[link_name = "\u{1}aws_lc_..."]`.
The `\u{1}` is an LLVM escape that cranelift takes literally, so a cranelift
build fails to link with doubled-underscore symbols
(`__aws_lc_0_45_0_SHA256`). aws/aws-lc-rs#1231, fixed by #1234 in 0.46.0.
Agent Muse's own lockfile is already on these versions. This one matters when
the agentmail binary is built by itself, as app-api's reload tests do under the
host repo's NIGHTLY_CRANELIFT=1. Lock-only: no other package moved, and the
suite passes on LLVM (494 tests).
The server no longer advertises capabilities.tasks, no tool declares execution.taskSupport (22 did), and tasks/list|get|result|cancel are gone, with the per-account serialization of destructive tasks, the notifications/tasks/status push and the related-task _meta. Agent Muse, which embeds this server, measured the task surface unused, and every rmcp 3.x release removes the MCP 2025-11-25 task model. A task-augmented tools/call is refused and the tool never runs; long operations still report progress and honour notifications/cancelled. Also: the fresh_logins / idle_reuses counters are labelled pool-wide (pool_fresh_logins / pool_idle_reuses in the log lines; ConnectionStats documents that they cover every account and reset with the pool). Breaking for a 0.x crate, hence 0.8.0.
rmcp =2.2.0 -> =3.5.1, exact-pinned to the embedding Agent Muse workspace's copy so the app links one rmcp. The revision does not move with the SDK (rmcp 3.5.0 made LATEST and Default 2026-07-28): get_info names PROTOCOL_VERSION (2025-11-25), and supported_protocol_versions() = known_up_to(PROTOCOL_VERSION) bounds initialize, server/discover and per-request _meta. A 2026-07-28 probe is refused -32022 with that list, which a probing client reads as legacy; discover itself answers method-not-found, so a probe at a 2025 version cannot start the 2026 stateless lifecycle at 2025-11-25. Mechanical: ServerInfo -> ServerConfig; the Meta extractor -> RequestMetaObject; schema_for_output returns the schema (no Result; the object-root guard it carried is tools_list_has_36_annotated_tools); the read_resource response enum; ListToolsResult's new fields defaulted. A task-augmented tools/call now runs as a plain call (rmcp 3.x has no `task` param), the 2025-11-25 rule for a server without the capability; the test flips from a_task_augmented_call_is_refused_never_run. New tests: a_2026_07_28_client_is_negotiated_down_to_2025_11_25, server_discover_lists_only_versions_up_to_2025_11_25.
Both lifecycles from one server. supported_protocol_versions() is known_up_to(MAX_PROTOCOL_VERSION), named as 2026-07-28 in the source, never rmcp's LATEST. initialize answers LATEST_WITH_INITIALIZE (2025-11-25), also to a client asking for 2026-07-28. server/discover is answered only at a revision without a handshake and lists only those (2026-07-28); at a 2025 version it is method-not-found, so no client runs the stateless lifecycle at a revision that defines none. A 2025 client sees what 0.8.0 sent, plus the tasks extension. Tasks return as the io.modelcontextprotocol/tasks extension, offered on both revisions and promoted late (src/mcp/tasks.rs). A call becomes a task only when: - the client declared the extension, - the tool is one of the 22 long-running ones (0.8.0's task list), - it is still running after 2 seconds, - and admission allows it (32 running, 1,024 held). Everything else is answered inline exactly as before. Each task lives 4 hours and suggests 1-second polls. - The operation runs on a child of the connection's lifetime token, never the request's: rmcp cancels a request's token as it sends the response, and a task's response is its handle (the SEP-1686 self-cancel bug). - tasks/cancel is cooperative. A task whose tool saw it through its CancelFn is cancelled; one that finished first is completed. A panic fails the task instead of leaving it working until the TTL. - After promotion, progress is the task's status message plus _meta["io.agentmuse/progress"] on tasks/get. - Closing the connection cancels every operation, waits 5 seconds, then aborts what is left. Library: the ten mutations that take a CancelFn leave the per-account queue when it fires (lock_account_mutation_unless_cancelled), instead of when the run ahead of them ends. Tests: the runtime (inline under budget, outliving the request token, cancel before and after promotion, finishing despite a cancel, panic before and after, TTL abort, progress, admission, shutdown), a promoted task polled to its result over the JSON-RPC wire, discover at 2026 and 2025, an unknown revision, tasks/* without the extension, and an unknown task id. Docs: MCP.md (two lifecycles; Tasks), README, DESIGN, DECISIONS (two 0.9.0 entries), CHANGELOG.
discover listed only the handshake-free revisions (2026-07-28), to keep a stateless client off 2025. But the version list a -32022 gives and the one discover gives are one fact, and the official conformance suite holds them to it: sep-2575-server-unsupported-version-error requires every version in a -32022 to appear in discover's supportedVersions. The gate on discover itself (method-not-found at a 2025 version) is what keeps a stateless client off 2025, and it stays. Test, MCP.md, CHANGELOG and the 0.9.0 decision follow.
rmcp =3.5.1 -> =3.6.0, exact-pinned to the embedding Agent Muse workspace's copy so the app links one rmcp. No API this crate uses changed (ServerHandler, the macros and the transports are identical), and the protocol constants are unchanged. What 3.6.0 changes here: a single incoming stdio message over 16 MiB is dropped (async_rw), a JSON-RPC message carrying both result and error, or method with an unreadable id, is rejected, and server-to-client requests on a 2026-07-28 HTTP session are refused at the source.
TaskRuntime::shutdown asked every operation to stop, gave them five seconds, then had rmcp drop the tasks, and dropping an operation aborted it wherever it was. One chunk of a bulk move on a slow server can take longer than that, so a closing connection (a mail-account edit, a reload) could cut a move between its COPY and its EXPUNGE. Dropped while the connection shuts down, an operation is now detached: its token is already cancelled, so it finishes the chunk it is in and stops at its next check. Any other drop (rmcp's TTL, the one hard stop) cancels the operation's token before aborting it, so work an abort cannot reach stops too.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
=3.7.0, exact-pinned to the Agent Muse workspace's rmcp, so the app links one copy.4ef0cd9, the commit Agent Muse beta points at). Upstream changed onlyCargo.lockhere, so each commit's lockfile is regenerated from its parent's, with rmcp-macros held at rmcp's version.Changes
8bef363feat!: retire MCP tasks (SEP-1686); 0.8.0. The 22execution(task_support)attributes, the five task handlers,tasks.rsand the task capability go.5314afcbuild: rmcp 3.5.1. The protocol stays MCP 2025-11-25.be381a5feat: MCP 2026-07-28 alongside 2025-11-25, with SEP-2663 tasks; 0.9.0.supported_protocol_versionsis bounded byMAX_PROTOCOL_VERSION.initializeanswers 2025-11-25, andserver/discoveranswers only at 2026-07-28.fresh_logins/idle_reusescounters are labelled pool-wide.6d8e44cfix:server/discoverlists every supported revision, as a-32022does.0ea3c21build: rmcp 3.6.0. No API this crate uses changed. 3.6.0 adds a 16 MiB stdio message cap, stricter JSON-RPC parsing, and refusal of server-to-client requests on 2026 HTTP sessions.Test plan
./ci-check.sh(fmt, clippy-D warningson all targets and features, build, docs, nextest) on 3.5.1 and again on 3.6.0: 491 passed, 2 skipped.cargo metadata --lockedholds at every commit. The 3.6.0 lockfile change is rmcp, rmcp-macros and rmcp's newlibcedge, and nothing else.Pairs with the Agent Muse superproject branch
weekendsuperhero/rmcp_3_5_0_upgrade, whose pointer commits reference these SHAs.