feat(mcp): adopt canonical MCP JSON-RPC bodies over NATS - #514
Conversation
…astructure Squashes the telegram branch history into one commit so it lands as a single reviewable unit rather than 113 incremental commits accumulated during development. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
The MCP 2026-07-28 transport model requires custom transports to preserve the JSON-RPC message format and treat the body as the source of truth, which the prior content-mode encoding cannot satisfy. Making the NATS body canonical JSON-RPC lets MCP messages pass through generic JSON-RPC infrastructure without a private reconstruction step. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
PR SummaryHigh Risk Overview The HTTP→NATS proxy is reworked on rmcp 3 ( Smaller changes: mise Reviewed by Cursor Bugbot for commit fda0a8b. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
Warning Review limit reached
Next review available in: 24 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
WalkthroughThis PR introduces canonical JSON-RPC bodies over NATS, typed MCP transport headers, expanded MCP method and subject routing, proxy metadata restoration, updated server-info handling, comprehensive transport tests, workspace dependency changes, ADR documentation, and a local Rust lint task. ChangesCanonical MCP transport
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Paramless lifecycle messages such as ping and notifications/initialized must survive a Message-based encode/decode cycle; the codec was rejecting them. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
…-upgrade Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com> # Conflicts: # rsworkspace/Cargo.lock
There was a problem hiding this comment.
Actionable comments posted: 16
🧹 Nitpick comments (4)
rsworkspace/crates/mcp/mcp-nats/src/transport.rs (1)
332-359: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low valueCustom suffix decoding accepts base64 that decodes to a method containing subject separators or an empty-ish string.
method_from_custom_suffixreturns any UTF-8 decode result, so a crafted subject can yield methods likea.bor control characters. It's currently safe because the canonical codec compares the projection against the body method, but a cheap guard keeps the invariant local (reject decoded methods that don't re-encode to the same suffix).♻️ Optional hardening
- String::from_utf8(bytes).map_err(|_| NatsTransportError::InvalidCustomMethodSuffix { - suffix: suffix.to_string(), - }) + let method = String::from_utf8(bytes).map_err(|_| NatsTransportError::InvalidCustomMethodSuffix { + suffix: suffix.to_string(), + })?; + if custom_method_suffix(&method) != suffix { + return Err(NatsTransportError::InvalidCustomMethodSuffix { + suffix: suffix.to_string(), + }); + } + Ok(method)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rsworkspace/crates/mcp/mcp-nats/src/transport.rs` around lines 332 - 359, Update method_from_custom_suffix to validate the decoded method against the canonical custom suffix representation before returning it. Re-encode the decoded UTF-8 method with custom_method_suffix and reject the result with InvalidCustomMethodSuffix when it differs from the input suffix, covering separator-containing, control-character, and empty-ish decoded values while preserving valid round trips.rsworkspace/crates/mcp/mcp-nats-server/src/runtime/tests.rs (1)
239-245: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueBusy-wait burns CPU; prefer a short sleep in the poll loop.
yield_now()spins the runtime for up to a second on failure.♻️ Sleep between polls
- tokio::task::yield_now().await; + tokio::time::sleep(std::time::Duration::from_millis(5)).await;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rsworkspace/crates/mcp/mcp-nats-server/src/runtime/tests.rs` around lines 239 - 245, Replace the tokio::task::yield_now() poll in the published_headers wait loop with a short asynchronous sleep, while keeping the existing timeout and condition unchanged. This should avoid continuously spinning the runtime during the wait.rsworkspace/crates/platform/jsonrpc-nats/src/tests/prop_tests.rs (1)
59-67: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFilter excludes the paramless case this PR specifically fixes.
encode_canonicaldrops"params": nullso paramless requests/notifications round-trip, butis_canonical_messagerejects them, so the property test never exercises that path. AllowingValue::Nullparams keeps the filter aligned with the codec contract.♻️ Widen the canonical filter
Message::Request { params, .. } | Message::Notification { params, .. } => { - params.is_object() || params.is_array() + params.is_object() || params.is_array() || params.is_null() }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rsworkspace/crates/platform/jsonrpc-nats/src/tests/prop_tests.rs` around lines 59 - 67, Update is_canonical_message for Message::Request and Message::Notification so params.is_null() is accepted alongside object and array values. Keep the existing canonical checks for success IDs and error messages unchanged, ensuring property tests include paramless messages handled by encode_canonical.rsworkspace/crates/chat/trogon-chat/src/agent_port.rs (1)
45-55: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftSplit this into operation-specific traits.
AgentPortcombines session creation, prompting, and cancellation. Define one trait per operation and compose them where a caller needs the full capability set.As per coding guidelines, “Prefer one trait per operation instead of a single trait containing multiple operations.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@rsworkspace/crates/chat/trogon-chat/src/agent_port.rs` around lines 45 - 55, Split AgentPort into separate operation-specific traits for session creation, prompting, and cancellation, preserving each method’s signature and associated error requirements. Define a composed capability using trait bounds or a supertrait for callers needing all three operations, and update references to AgentPort to use the appropriate individual or combined trait.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/adr/0041-canonical-mcp-jsonrpc-bodies-over-nats.md`:
- Line 82: Update the superseded ADR reference in the decision text to use the
existing relative Markdown link for ADR#0011, while preserving the “for MCP
only” scope and the following ACP/A2A statement.
In `@docs/architecture/multi-channel-agent-routing.md`:
- Line 20: Update every fenced diagram in this document to specify the text
language after the opening fence, including the referenced fences, so all
diagrams satisfy markdownlint MD040 while preserving their contents.
In `@rsworkspace/crates/chat/chat-bridge-telegram/Cargo.toml`:
- Around line 27-28: Replace the testcontainers-modules dev-dependency in the
Cargo.toml [dev-dependencies] section with the trogon-nats dependency configured
with its test-support feature, so NATS tests use the shared helpers and mocks.
In `@rsworkspace/crates/chat/chat-bridge-telegram/src/acp_port.rs`:
- Around line 76-81: Update acp_port::prompt to preserve event.attachments when
constructing PromptRequest: convert each inbound attachment into the appropriate
ACP content/resource block and append those blocks alongside the existing text
block. Also update Telegram event normalization so attachments are populated
from the inbound message instead of always using Vec::new().
In `@rsworkspace/crates/chat/chat-bridge-telegram/src/config.rs`:
- Around line 26-61: Define typed BridgeConfigError variants in config.rs and
return them from from_env for missing variables, invalid Telegram user IDs, and
invalid ACP prefixes instead of dynamic or formatted errors; in main.rs lines
53-58, add a runtime error variant retaining both the stream name and JetStream
source error, and in lines 135-138, retain the ACP initialization source error
in its own runtime variant, formatting errors only at the outer logging
boundary.
- Around line 6-22: Update BridgeConfig and its from_env constructor to replace
the raw String fields chat_prefix, inbound_stream, bot_account, and agent_id
with separate domain-specific value objects. Define independent validation and
construction for each value object, then parse and validate them once in
from_env before assembling BridgeConfig. Keep bot_token and unrelated
configuration fields unchanged.
In `@rsworkspace/crates/chat/chat-bridge-telegram/src/main.rs`:
- Around line 65-68: Update the acknowledgment flow in handle_message so
long-running self.port.prompt(...) turns do not remain unacked past ack_wait and
trigger JetStream redelivery. Prefer an in-progress acknowledgment heartbeat
during the prompt, preserving the final acknowledgment after Telegram sends;
alternatively, deduplicate prompt processing by message_ref so any redelivery
cannot execute the turn twice.
In `@rsworkspace/crates/chat/chat-bridge-telegram/src/outbound.rs`:
- Around line 9-12: The Outbound trait currently erases Telegram SDK error and
response types; replace it with separate typing and text-send operation traits
using associated error and success types that preserve teloxide Requester
classifications and response values. Update TelegramOutbound and its
callers/implementations to pass through the underlying SDK results without
anyhow conversion, while retaining the existing chat_id and text inputs.
In `@rsworkspace/crates/chat/chat-bridge-telegram/src/pipeline_tests.rs`:
- Around line 10-35: Replace the local NatsServer implementation and direct
async_nats::connect setup in the Telegram pipeline tests with
trogon_nats::test_support::JetStreamTestServer, enabling the trogon-nats
test-support feature in the crate configuration. Update the affected test setup
and connection usage, including the code around the referenced later lines, to
use the helper’s provided server URL and lifecycle management.
In `@rsworkspace/crates/chat/chat-bridge-telegram/src/pipeline.rs`:
- Around line 27-29: Update the pipeline flow around ack and the
message-processing logic (including the affected 82–131 range) to persist
event-level idempotency state keyed by message_ref, propagate that idempotency
key to the agent, and prevent replay after send or acknowledgment failures.
Replace broad prompt-error retries with retries only for a typed error proving
the first prompt did not execute, and introduce/use a PipelineError enum that
preserves underlying source errors rather than formatting them into anyhow
strings; keep JetStream acknowledgment behavior consistent with the persisted
state.
In `@rsworkspace/crates/chat/chat-bridge-telegram/src/render.rs`:
- Around line 61-66: Update the buffered-output handling around the session
buffer entry to enforce a maximum size for streamed text. When appending would
exceed the limit, truncate or cancel the buffered output and record an explicit
user-visible failure, ensuring the session cannot continue growing the buffer
indefinitely.
In `@rsworkspace/crates/chat/trogon-chat/Cargo.toml`:
- Line 10: Replace the direct async-nats dependency in
rsworkspace/crates/chat/trogon-chat/Cargo.toml and refactor the chat store
construction and types in rsworkspace/crates/chat/trogon-chat/src/store.rs to
use trogon-nats’s NatsJetStreamClient and mock abstractions for JetStream/KV
behavior. In rsworkspace/crates/mcp/mcp-nats-server/Cargo.toml, remove the
async-nats dev-dependency; retain direct async-nats message/header imports in
tests while using trogon-nats mocks for client behavior.
In `@rsworkspace/crates/chat/trogon-chat/src/endpoint.rs`:
- Around line 21-42: Remove derived Deserialize from Endpoint, PrincipalId,
AgentSessionId, AgentId, ConversationId, ConversationRecord, and
InboundChatEvent at rsworkspace/crates/chat/trogon-chat/src/endpoint.rs:21-42
and :72-81, agent_port.rs:9-15, conversation.rs:7-36, event.rs:26-36, and
store.rs:71-104; introduce or reuse corresponding *Wire/separate record types
for persisted and inbound payloads, then convert them through the existing
constructors or validation paths so unsafe endpoint tokens and arbitrary
identifiers cannot enter domain types.
In `@rsworkspace/crates/chat/trogon-chat/src/store.rs`:
- Around line 117-123: The independent writes in create_conversation and
link_endpoint must become recoverable and idempotent: ensure retries reconcile
or reuse the already-created conversation/principal rather than generating or
binding a different record when the second write fails. Update both methods to
use a transactional, single-record, CAS/reread-on-failure, or equivalent
reconciliation pattern covering conversations/<id> with bindings and
principals/<principal> with endpoints.
- Around line 44-55: Update ensure_bucket so only the JetStream not-found error
from get_key_value triggers bucket creation, using the same source check as
trogon-decider-nats. Propagate communication, authorization, and other lookup
failures directly instead of mapping them to ChatStoreError::CreateBucket; keep
the existing creation path for the absent-bucket case.
In `@rsworkspace/crates/mcp/mcp-nats-server/src/runtime.rs`:
- Around line 189-194: Update unexpected_result so the client-facing ErrorData
message includes only the method and ServerResult variant name, not the full
{result:?} payload; log the complete debug representation locally for
diagnostics if existing logging facilities are available.
---
Nitpick comments:
In `@rsworkspace/crates/chat/trogon-chat/src/agent_port.rs`:
- Around line 45-55: Split AgentPort into separate operation-specific traits for
session creation, prompting, and cancellation, preserving each method’s
signature and associated error requirements. Define a composed capability using
trait bounds or a supertrait for callers needing all three operations, and
update references to AgentPort to use the appropriate individual or combined
trait.
In `@rsworkspace/crates/mcp/mcp-nats-server/src/runtime/tests.rs`:
- Around line 239-245: Replace the tokio::task::yield_now() poll in the
published_headers wait loop with a short asynchronous sleep, while keeping the
existing timeout and condition unchanged. This should avoid continuously
spinning the runtime during the wait.
In `@rsworkspace/crates/mcp/mcp-nats/src/transport.rs`:
- Around line 332-359: Update method_from_custom_suffix to validate the decoded
method against the canonical custom suffix representation before returning it.
Re-encode the decoded UTF-8 method with custom_method_suffix and reject the
result with InvalidCustomMethodSuffix when it differs from the input suffix,
covering separator-containing, control-character, and empty-ish decoded values
while preserving valid round trips.
In `@rsworkspace/crates/platform/jsonrpc-nats/src/tests/prop_tests.rs`:
- Around line 59-67: Update is_canonical_message for Message::Request and
Message::Notification so params.is_null() is accepted alongside object and array
values. Keep the existing canonical checks for success IDs and error messages
unchanged, ensuring property tests include paramless messages handled by
encode_canonical.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 07160b1b-665c-44a7-b691-5a9e1979fe74
⛔ Files ignored due to path filters (1)
rsworkspace/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (55)
.github/canary-container-services.json.github/workflows/canary-container-images.yml.gitignoredevops/docker/compose/compose.ymldevops/docker/compose/services/chat-bridge-telegram/Dockerfiledocs/adr/0041-canonical-mcp-jsonrpc-bodies-over-nats.mddocs/adr/index.mddocs/architecture/multi-channel-agent-routing.mdrsworkspace/Cargo.tomlrsworkspace/crates/chat/chat-bridge-telegram/Cargo.tomlrsworkspace/crates/chat/chat-bridge-telegram/src/acp_port.rsrsworkspace/crates/chat/chat-bridge-telegram/src/config.rsrsworkspace/crates/chat/chat-bridge-telegram/src/main.rsrsworkspace/crates/chat/chat-bridge-telegram/src/outbound.rsrsworkspace/crates/chat/chat-bridge-telegram/src/parse.rsrsworkspace/crates/chat/chat-bridge-telegram/src/pipeline.rsrsworkspace/crates/chat/chat-bridge-telegram/src/pipeline_tests.rsrsworkspace/crates/chat/chat-bridge-telegram/src/render.rsrsworkspace/crates/chat/trogon-chat/Cargo.tomlrsworkspace/crates/chat/trogon-chat/src/agent_port.rsrsworkspace/crates/chat/trogon-chat/src/conversation.rsrsworkspace/crates/chat/trogon-chat/src/endpoint.rsrsworkspace/crates/chat/trogon-chat/src/event.rsrsworkspace/crates/chat/trogon-chat/src/lib.rsrsworkspace/crates/chat/trogon-chat/src/render.rsrsworkspace/crates/chat/trogon-chat/src/store.rsrsworkspace/crates/mcp/mcp-nats-server/Cargo.tomlrsworkspace/crates/mcp/mcp-nats-server/src/runtime.rsrsworkspace/crates/mcp/mcp-nats-server/src/runtime/tests.rsrsworkspace/crates/mcp/mcp-nats/Cargo.tomlrsworkspace/crates/mcp/mcp-nats/src/lib.rsrsworkspace/crates/mcp/mcp-nats/src/mcp_transport_headers.rsrsworkspace/crates/mcp/mcp-nats/src/nats/parsing.rsrsworkspace/crates/mcp/mcp-nats/src/nats/parsing/tests.rsrsworkspace/crates/mcp/mcp-nats/src/nats/subjects/server/discover.rsrsworkspace/crates/mcp/mcp-nats/src/nats/subjects/server/mod.rsrsworkspace/crates/mcp/mcp-nats/src/nats/subjects/server/subscriptions_acknowledged.rsrsworkspace/crates/mcp/mcp-nats/src/nats/subjects/server/subscriptions_listen.rsrsworkspace/crates/mcp/mcp-nats/src/nats/subjects/server/task_status.rsrsworkspace/crates/mcp/mcp-nats/src/nats/subjects/server/update_task.rsrsworkspace/crates/mcp/mcp-nats/src/nats/subjects/tests.rsrsworkspace/crates/mcp/mcp-nats/src/transport.rsrsworkspace/crates/mcp/mcp-nats/src/transport/tests.rsrsworkspace/crates/mcp/mcp-nats/src/wire.rsrsworkspace/crates/mcp/mcp-nats/src/wire/tests.rsrsworkspace/crates/mcp/mcp-nats/tests/transport.rsrsworkspace/crates/platform/jsonrpc-nats/src/codec/canonical.rsrsworkspace/crates/platform/jsonrpc-nats/src/codec/mod.rsrsworkspace/crates/platform/jsonrpc-nats/src/error.rsrsworkspace/crates/platform/jsonrpc-nats/src/lib.rsrsworkspace/crates/platform/jsonrpc-nats/src/tests.rsrsworkspace/crates/platform/jsonrpc-nats/src/tests/prop_tests.rsrsworkspace/crates/platform/jsonrpc-nats/src/transport.rsrsworkspace/crates/platform/trogon-nats/src/mocks.rsrsworkspace/crates/platform/trogon-telemetry/src/service_name.rs
Unblocks the ADR-reference and markdownlint CI checks on PR #514. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
The client-facing error embedded ServerResult debug output, which can carry tool/prompt content; keep it in a local log instead. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@rsworkspace/crates/mcp/mcp-nats-server/src/runtime.rs`:
- Around line 189-197: Update unexpected_result so the warning no longer logs
the full ServerResult payload via ?result; retain the method and, if useful, log
only a non-sensitive result classification. Keep the client-facing ErrorData
message unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 94ae0412-8895-4a3d-a993-fc2733f2a73a
📒 Files selected for processing (3)
docs/adr/0041-canonical-mcp-jsonrpc-bodies-over-nats.mddocs/architecture/multi-channel-agent-routing.mdrsworkspace/crates/mcp/mcp-nats-server/src/runtime.rs
The unexpected-result warning logged the full ServerResult debug, which can carry tool/prompt content into centralized logs; log only the method and an opaque variant discriminant. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Merging main advanced acp-nats (client::run now takes Arc) and gated trogon-nats NatsJetStreamClient out of coverage builds; realign the bridge and add the missing crate licenses so build, test, and lint pass. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Code Coverage SummaryDetailsDiff against mainResults for commit: fda0a8b Minimum allowed coverage is ♻️ This comment has been updated with latest results |
teloxide default features pull native-tls, which feature-unifies onto the shared reqwest across the workspace and trips the ADR#0015 native-tls/openssl ban. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
…-upgrade Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com> # Conflicts: # rsworkspace/Cargo.lock
The chat crates carried formatting drift that fails the cargo fmt --check lint gate. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
The branch's code predated the policy lints merged from main: extract inline test modules to files, hoist function-local use statements, and move TEXT_CHUNK_LIMIT into a constants module. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 5 total unresolved issues (including 4 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 11e124d. Configure here.
Runs license, deny bans, fmt, repo dylint policy lints, and clippy workspace-wide in one command so the full lint gate can be verified locally before pushing. Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.config/mise/tasks/rust-lint:
- Around line 14-16: Make the workspace setup in the rust-lint script fail
closed: enable errexit or explicitly validate the `cd "$RSWS"` command, so the
script stops immediately when entering `rsworkspace` fails and never runs
subsequent cargo commands from the caller’s directory.
- Around line 69-73: Update the toolchain configuration parsing around
DYLINT_TOOLCHAIN_CONFIG to capture and validate the Python parser’s exit status
before accessing the array, and verify the required channel and components TOML
keys before continuing. On any parsing or validation failure, emit the
policy-lint step’s RESULT: FAIL message and exit through the existing failure
path. Use a Python parser compatible with the supported runtime, including
environments older than 3.11, instead of relying unconditionally on tomllib.
- Around line 43-52: Update the Cargo manifest parsing in the rust-lint
package-license check to parse TOML semantically, using the `[package].license`
value rather than matching the exact `license = "Apache-2.0"` line. Accept
equivalent formatting and inline comments, while separately rejecting manifests
that use `license.workspace = true` when a literal per-crate license declaration
is required; preserve the existing `pkg.get("license")` validation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 51ef9b73-fd21-41fa-a6a5-1b7a55fefae3
⛔ Files ignored due to path filters (1)
rsworkspace/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
.config/mise/tasks/rust-lintrsworkspace/Cargo.tomlrsworkspace/crates/mcp/mcp-nats/src/nats/parsing/tests.rsrsworkspace/crates/mcp/mcp-nats/src/transport/tests.rs
💤 Files with no reviewable changes (1)
- rsworkspace/Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (2)
- rsworkspace/crates/mcp/mcp-nats/src/nats/parsing/tests.rs
- rsworkspace/crates/mcp/mcp-nats/src/transport/tests.rs
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>

Mcp-*headers stay derived projections that must agree with the body.