1#[cfg(feature = "unstable_protocol_v2")]
7pub(crate) const UNKNOWN_IMPLEMENTATION: &str = "unknown";
8
9#[cfg(feature = "unstable_protocol_v2")]
19pub(crate) fn normalize_newer_v2_initialize_request(
20 params: &serde_json::Map<String, serde_json::Value>,
21) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
22 use crate::{schema::ProtocolVersion, v2::schema as v2};
23 use serde::Deserialize as _;
24
25 let mut params = params.clone();
26 let info_parses = params
27 .get("info")
28 .is_some_and(|info| v2::Implementation::deserialize(info).is_ok());
29 if !info_parses {
30 let unknown = v2::Implementation::new(UNKNOWN_IMPLEMENTATION, UNKNOWN_IMPLEMENTATION);
31 params.insert(
32 "info".into(),
33 serde_json::to_value(unknown).map_err(crate::Error::into_internal_error)?,
34 );
35 }
36 retain_parsable_capabilities::<v2::ClientCapabilities>(&mut params, "capabilities");
37 retain_object(&mut params, "_meta");
38 params.insert(
39 "protocolVersion".into(),
40 serde_json::to_value(ProtocolVersion::V2).map_err(crate::Error::into_internal_error)?,
41 );
42 let initialize: v2::InitializeRequest =
43 serde_json::from_value(serde_json::Value::Object(params))?;
44 let serde_json::Value::Object(normalized) =
45 serde_json::to_value(initialize).map_err(crate::Error::into_internal_error)?
46 else {
47 return Err(crate::util::internal_error(
48 "initialize params did not serialize to an object",
49 ));
50 };
51 Ok(normalized)
52}
53
54fn retain_parsable_capabilities<T: serde::de::DeserializeOwned>(
57 params: &mut serde_json::Map<String, serde_json::Value>,
58 key: &str,
59) {
60 if !retain_object(params, key) {
61 return;
62 }
63 if let Some(serde_json::Value::Object(capabilities)) = params.get_mut(key) {
64 capabilities.retain(|name, value| {
65 let single = serde_json::Map::from_iter([(name.clone(), value.clone())]);
66 serde_json::from_value::<T>(serde_json::Value::Object(single)).is_ok()
67 });
68 }
69}
70
71fn retain_object(params: &mut serde_json::Map<String, serde_json::Value>, key: &str) -> bool {
73 match params.get(key) {
74 Some(serde_json::Value::Object(_)) => true,
75 Some(_) => {
76 params.remove(key);
77 false
78 }
79 None => false,
80 }
81}
82
83pub(crate) fn normalize_v1_initialize_request(
93 params: &serde_json::Value,
94) -> Result<serde_json::Map<String, serde_json::Value>, crate::Error> {
95 use crate::{schema::ProtocolVersion, v1::schema as v1};
96 use serde::Deserialize as _;
97
98 let info = params
99 .get("info")
100 .and_then(|info| v1::Implementation::deserialize(info).ok());
101 let mut capabilities = serde_json::Map::new();
102 if let Some(source) = params
103 .get("capabilities")
104 .and_then(serde_json::Value::as_object)
105 {
106 for key in ["auth", "elicitation", "_meta"] {
107 if let Some(value) = source.get(key) {
108 capabilities.insert(key.into(), value.clone());
109 }
110 }
111 }
112 if let Some(auth) = capabilities
113 .get_mut("auth")
114 .and_then(serde_json::Value::as_object_mut)
115 {
116 let terminal = auth
117 .remove("terminal")
118 .is_some_and(|value| value.is_object());
119 auth.insert("terminal".into(), terminal.into());
120 }
121 capabilities.insert(
122 "session".into(),
123 serde_json::json!({ "configOptions": { "boolean": {} } }),
124 );
125 let mut target = serde_json::Map::from_iter([
126 (
127 "protocolVersion".to_owned(),
128 serde_json::to_value(ProtocolVersion::V1).map_err(crate::Error::into_internal_error)?,
129 ),
130 ("clientCapabilities".to_owned(), capabilities.into()),
131 ]);
132 if let Some(info) = info {
133 target.insert(
134 "clientInfo".into(),
135 serde_json::to_value(info).map_err(crate::Error::into_internal_error)?,
136 );
137 }
138 if let Some(meta) = params.get("_meta") {
139 target.insert("_meta".into(), meta.clone());
140 }
141 retain_parsable_capabilities::<v1::ClientCapabilities>(&mut target, "clientCapabilities");
142 retain_object(&mut target, "_meta");
143 let initialize: v1::InitializeRequest =
144 serde_json::from_value(serde_json::Value::Object(target))?;
145 let serde_json::Value::Object(normalized) =
146 serde_json::to_value(initialize).map_err(crate::Error::into_internal_error)?
147 else {
148 return Err(crate::util::internal_error(
149 "initialize params did not serialize to an object",
150 ));
151 };
152 Ok(normalized)
153}
154
155#[cfg(feature = "unstable_protocol_v2")]
165#[derive(Debug, Clone, PartialEq, Eq)]
166#[non_exhaustive]
167pub enum InitializeOutcome {
168 Initialized(crate::v2::schema::InitializeResponse),
170 LowerVersion(InitializationVersionMismatch),
176}
177
178#[cfg(feature = "unstable_protocol_v2")]
189#[derive(Debug, Clone, PartialEq, Eq)]
190#[non_exhaustive]
191pub struct InitializationVersionMismatch {
192 pub requested: crate::schema::ProtocolVersion,
194 pub response: SelectedInitializeResponse,
197}
198
199#[cfg(feature = "unstable_protocol_v2")]
200impl InitializationVersionMismatch {
201 #[must_use]
206 pub fn new(
207 requested: crate::schema::ProtocolVersion,
208 response: SelectedInitializeResponse,
209 ) -> Self {
210 Self {
211 requested,
212 response,
213 }
214 }
215
216 #[must_use]
219 pub fn selected(&self) -> crate::schema::ProtocolVersion {
220 self.response.protocol_version()
221 }
222}
223
224#[cfg(feature = "unstable_protocol_v2")]
225impl std::fmt::Display for InitializationVersionMismatch {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 write!(
228 f,
229 "requested ACP protocol version {} but peer selected {}",
230 self.requested,
231 self.selected(),
232 )
233 }
234}
235
236#[cfg(feature = "unstable_protocol_v2")]
244#[derive(Debug, Clone, PartialEq, Eq)]
245#[non_exhaustive]
246pub enum SelectedInitializeResponse {
247 V1(crate::v1::schema::InitializeResponse),
249}
250
251#[cfg(feature = "unstable_protocol_v2")]
252impl SelectedInitializeResponse {
253 #[must_use]
255 pub fn protocol_version(&self) -> crate::schema::ProtocolVersion {
256 match self {
257 Self::V1(response) => response.protocol_version,
258 }
259 }
260
261 fn from_json(
264 version: crate::schema::ProtocolVersion,
265 value: serde_json::Value,
266 ) -> Option<Result<Self, crate::Error>> {
267 (version == crate::schema::ProtocolVersion::V1).then(|| {
268 <crate::v1::schema::InitializeResponse as crate::JsonRpcResponse>::from_json(
269 "initialize",
270 value,
271 )
272 .map(Self::V1)
273 })
274 }
275}
276
277#[cfg(feature = "unstable_protocol_v2")]
278impl crate::JsonRpcResponse for InitializeOutcome {
279 fn into_json(self, method: &str) -> Result<serde_json::Value, crate::Error> {
280 match self {
281 Self::Initialized(response) => response.into_json(method),
282 Self::LowerVersion(mismatch) => match mismatch.response {
283 SelectedInitializeResponse::V1(response) => response.into_json(method),
284 },
285 }
286 }
287
288 fn from_json(method: &str, value: serde_json::Value) -> Result<Self, crate::Error> {
289 let selected: crate::schema::ProtocolVersion = value
290 .get("protocolVersion")
291 .cloned()
292 .map(serde_json::from_value)
293 .transpose()
294 .ok()
295 .flatten()
296 .ok_or_else(|| {
297 crate::Error::invalid_params()
298 .data("initialize.protocolVersion must be a valid ACP protocol version")
299 })?;
300 let requested = crate::schema::ProtocolVersion::V2;
301 if selected == requested {
302 return crate::v2::schema::InitializeResponse::from_json(method, value)
303 .map(Self::Initialized);
304 }
305 match SelectedInitializeResponse::from_json(selected, value) {
306 Some(response) if selected < requested => Ok(Self::LowerVersion(
307 InitializationVersionMismatch::new(requested, response?),
308 )),
309 _ => Err(crate::Error::invalid_request().data(format!(
310 "unsupported ACP protocol version {selected} in initialize response",
311 ))),
312 }
313 }
314}
315
316#[cfg(feature = "unstable_protocol_v2")]
321#[derive(Debug, Clone)]
322pub(crate) struct InitializeNegotiation(pub(crate) crate::v2::schema::InitializeRequest);
323
324#[cfg(feature = "unstable_protocol_v2")]
325impl crate::JsonRpcMessage for InitializeNegotiation {
326 fn matches_method(method: &str) -> bool {
327 crate::v2::schema::InitializeRequest::matches_method(method)
328 }
329
330 fn method(&self) -> &str {
331 self.0.method()
332 }
333
334 fn to_untyped_message(&self) -> Result<crate::UntypedMessage, crate::Error> {
335 self.0.to_untyped_message()
336 }
337
338 fn parse_message(method: &str, params: &serde_json::Value) -> Result<Self, crate::Error> {
339 crate::v2::schema::InitializeRequest::parse_message(method, params).map(Self)
340 }
341}
342
343#[cfg(feature = "unstable_protocol_v2")]
344impl crate::JsonRpcRequest for InitializeNegotiation {
345 type Response = InitializeOutcome;
346}
347
348mod imp {
349 use std::sync::{Arc, Mutex};
350
351 use crate::RawJsonRpcId;
352 use crate::schema::ProtocolVersion;
353 use crate::{UntypedMessage, role::RemoteStyle};
354
355 #[derive(Clone, Copy, Debug)]
356 pub(crate) enum ProtocolMode {
357 Disabled,
358 Acp(AcpProtocolMode),
359 }
360
361 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
362 pub(crate) struct AcpProtocolMode {
363 api: ProtocolVersionKind,
364 initialize_surface: InitializeSurface,
365 initialization_role: InitializationRole,
366 requires_initialize: bool,
371 }
372
373 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
374 enum InitializeSurface {
375 Peer,
376 Proxy,
377 }
378
379 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
380 #[cfg_attr(not(feature = "unstable_protocol_v2"), allow(dead_code))]
381 enum InitializationRole {
382 Unchecked,
387 Initiator,
388 Responder,
389 }
390
391 impl AcpProtocolMode {
392 fn is_incoming_initialize_request(self, method: &str) -> bool {
393 match self.initialize_surface {
394 InitializeSurface::Peer => method == "initialize",
395 InitializeSurface::Proxy => method == "proxy/initialize",
396 }
397 }
398
399 fn is_incoming_initialize_response(method: &str) -> bool {
400 method == "initialize"
404 }
405
406 fn is_outgoing_initialize_response(self, method: &str) -> bool {
407 match self.initialize_surface {
408 InitializeSurface::Peer => method == "initialize",
409 InitializeSurface::Proxy => method == "proxy/initialize",
410 }
411 }
412 }
413
414 impl ProtocolMode {
415 pub(crate) fn disabled() -> Self {
416 Self::Disabled
417 }
418
419 pub(crate) fn v1_agent() -> Self {
420 Self::Acp(AcpProtocolMode {
421 api: ProtocolVersionKind::V1,
422 initialize_surface: InitializeSurface::Peer,
423 initialization_role: InitializationRole::Unchecked,
424 requires_initialize: false,
425 })
426 }
427
428 pub(crate) fn require_initialize(self) -> Self {
432 match self {
433 Self::Acp(mode)
434 if mode.api == ProtocolVersionKind::V1
435 && mode.initialize_surface == InitializeSurface::Peer =>
436 {
437 Self::Acp(AcpProtocolMode {
438 requires_initialize: true,
439 ..mode
440 })
441 }
442 mode => mode,
443 }
444 }
445
446 pub(crate) fn v1_client() -> Self {
447 Self::Acp(AcpProtocolMode {
448 api: ProtocolVersionKind::V1,
449 initialize_surface: InitializeSurface::Peer,
450 initialization_role: InitializationRole::Unchecked,
451 requires_initialize: false,
452 })
453 }
454
455 pub(crate) fn v1_proxy() -> Self {
456 Self::Acp(AcpProtocolMode {
457 api: ProtocolVersionKind::V1,
458 initialize_surface: InitializeSurface::Proxy,
459 initialization_role: InitializationRole::Unchecked,
460 requires_initialize: false,
461 })
462 }
463
464 #[cfg(feature = "unstable_protocol_v2")]
465 pub(crate) fn v2_agent() -> Self {
466 Self::Acp(AcpProtocolMode {
467 api: ProtocolVersionKind::V2,
468 initialize_surface: InitializeSurface::Peer,
469 initialization_role: InitializationRole::Responder,
470 requires_initialize: false,
471 })
472 }
473
474 #[cfg(feature = "unstable_protocol_v2")]
475 pub(crate) fn v2_client() -> Self {
476 Self::Acp(AcpProtocolMode {
477 api: ProtocolVersionKind::V2,
478 initialize_surface: InitializeSurface::Peer,
479 initialization_role: InitializationRole::Initiator,
480 requires_initialize: false,
481 })
482 }
483
484 #[cfg(feature = "unstable_protocol_v2")]
485 pub(crate) fn v2_proxy() -> Self {
486 Self::Acp(AcpProtocolMode {
487 api: ProtocolVersionKind::V2,
488 initialize_surface: InitializeSurface::Proxy,
489 initialization_role: InitializationRole::Unchecked,
490 requires_initialize: false,
491 })
492 }
493
494 pub(crate) fn merge(self, other: Self) -> Self {
495 match (self, other) {
496 (Self::Disabled, other) => other,
497 (this, Self::Disabled) => this,
498 (Self::Acp(this), Self::Acp(other)) => {
499 assert_eq!(
500 this.api, other.api,
501 "cannot merge ACP builders with different API protocol versions; \
502 handler chains share a single API surface",
503 );
504 assert_eq!(
505 this.initialize_surface, other.initialize_surface,
506 "cannot merge standard ACP and proxy ACP builders; \
507 handler chains share one initialization surface",
508 );
509 assert_eq!(
510 this.initialization_role, other.initialization_role,
511 "cannot merge ACP builders with different initialization roles; \
512 handler chains share one connection lifecycle",
513 );
514 Self::Acp(AcpProtocolMode {
515 requires_initialize: this.requires_initialize || other.requires_initialize,
516 ..this
517 })
518 }
519 }
520 }
521
522 pub(crate) fn api_protocol_version(self) -> Option<ProtocolVersion> {
523 match self {
524 Self::Disabled => None,
525 Self::Acp(mode) => Some(mode.api.as_protocol_version()),
526 }
527 }
528 }
529
530 #[derive(Clone, Debug)]
531 pub(crate) struct ProtocolCompat {
532 mode: Option<AcpProtocolMode>,
533 state: Arc<Mutex<ProtocolState>>,
534 }
535
536 #[derive(Debug)]
537 struct ProtocolState {
538 negotiated: ProtocolVersionKind,
539 pending_initialize: Option<ProtocolVersionKind>,
540 incoming_initialize_id: Option<RawJsonRpcId>,
541 initialization: InitializationState,
542 initialized: bool,
544 repeatable: bool,
551 }
552
553 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
554 enum InitializationState {
555 Unchecked,
556 Uninitialized,
557 Initializing,
558 Ready,
559 }
560
561 enum InitializeResponseCheck {
564 Negotiated(ProtocolVersionKind),
565 #[cfg_attr(not(feature = "unstable_protocol_v2"), allow(dead_code))]
567 LowerVersion,
568 }
569
570 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
571 enum ProtocolVersionKind {
572 V1,
573 V2,
574 }
575
576 impl ProtocolVersionKind {
577 fn as_protocol_version(self) -> ProtocolVersion {
578 match self {
579 Self::V1 => ProtocolVersion::V1,
580 Self::V2 => ProtocolVersion::from(2_u16),
581 }
582 }
583
584 fn from_protocol_version(version: ProtocolVersion) -> Option<Self> {
585 if version == ProtocolVersion::V1 {
586 Some(Self::V1)
587 } else if version == ProtocolVersion::from(2_u16) {
588 Some(Self::V2)
589 } else {
590 None
591 }
592 }
593 }
594
595 impl ProtocolCompat {
596 pub(crate) fn new(mode: ProtocolMode) -> Self {
597 let mode = match mode {
598 ProtocolMode::Disabled => None,
599 ProtocolMode::Acp(mode) => Some(mode),
600 };
601 let negotiated = mode.map_or(ProtocolVersionKind::V1, |mode| mode.api);
602 let initialization = match mode.map(|mode| mode.initialization_role) {
603 Some(InitializationRole::Initiator | InitializationRole::Responder) => {
604 InitializationState::Uninitialized
605 }
606 Some(InitializationRole::Unchecked) | None => InitializationState::Unchecked,
607 };
608
609 Self {
610 mode,
611 state: Arc::new(Mutex::new(ProtocolState {
612 negotiated,
613 pending_initialize: None,
614 incoming_initialize_id: None,
615 initialization,
616 initialized: false,
617 repeatable: false,
618 })),
619 }
620 }
621
622 #[cfg(all(test, feature = "unstable_protocol_v2"))]
623 pub(crate) fn incoming_message(
624 &self,
625 message: UntypedMessage,
626 ) -> Result<UntypedMessage, crate::Error> {
627 self.incoming_request(&RawJsonRpcId::Number(0), message)
628 }
629
630 pub(crate) fn incoming_request(
631 &self,
632 id: &RawJsonRpcId,
633 message: UntypedMessage,
634 ) -> Result<UntypedMessage, crate::Error> {
635 let Some(mode) = self.mode else {
636 return Ok(message);
637 };
638
639 if mode.api == ProtocolVersionKind::V2 && *id == RawJsonRpcId::Null {
643 return Err(crate::jsonrpc::raw_id::null_v2_request_id());
644 }
645
646 if mode.is_incoming_initialize_request(message.method()) {
647 return self.incoming_initialize_request(mode, id, message);
648 }
649 if mode.initialize_surface == InitializeSurface::Proxy
650 && (message.method() == "initialize" || successor_encloses_initialize(&message))
651 {
652 return Err(invalid_proxy_initialize_direction());
653 }
654
655 if mode.requires_initialize && !self.has_initialized() {
656 return Err(initialization_required(message.method()));
657 }
658 self.ensure_initialized(message.method())?;
659 ensure_matching_protocol_version(
660 message.method(),
661 self.active_wire_version(),
662 mode.api,
663 )?;
664 if mode.initialization_role == InitializationRole::Responder {
665 self.close_repeat_window();
666 }
667 Ok(message)
668 }
669
670 pub(crate) fn outgoing_message(
671 &self,
672 mut message: UntypedMessage,
673 remote_style: RemoteStyle,
674 ) -> Result<UntypedMessage, crate::Error> {
675 let Some(mode) = self.mode else {
676 return Ok(message);
677 };
678
679 let wire_version = if let Some(params) =
680 outgoing_initialize_params(mode, remote_style, &mut message)?
681 {
682 set_protocol_version(params, mode.api)?;
683 validate_native_v2_initialize_request(mode, params)?;
684 self.begin_outgoing_initialize(mode)?;
685 mode.api
686 } else {
687 self.ensure_initialized(message.method())?;
688 self.active_wire_version()
689 };
690
691 ensure_matching_protocol_version(message.method(), mode.api, wire_version)?;
692 if mode.initialization_role == InitializationRole::Initiator {
693 self.close_repeat_window();
694 }
695 Ok(message)
696 }
697
698 pub(crate) fn incoming_notification(
699 &self,
700 message: UntypedMessage,
701 ) -> Result<Vec<UntypedMessage>, crate::Error> {
702 let Some(mode) = self.mode else {
703 return Ok(vec![message]);
704 };
705
706 self.ensure_notification_allowed(message.method())?;
707 ensure_matching_protocol_version(
708 message.method(),
709 self.active_wire_version(),
710 mode.api,
711 )?;
712 Ok(vec![message])
713 }
714
715 pub(crate) fn outgoing_notification(
716 &self,
717 message: UntypedMessage,
718 ) -> Result<Vec<UntypedMessage>, crate::Error> {
719 let Some(mode) = self.mode else {
720 return Ok(vec![message]);
721 };
722
723 self.ensure_notification_allowed(message.method())?;
724 ensure_matching_protocol_version(
725 message.method(),
726 mode.api,
727 self.active_wire_version(),
728 )?;
729 Ok(vec![message])
730 }
731
732 pub(crate) fn incoming_response(
738 &self,
739 method: &str,
740 accepts_lower_version: bool,
741 result: Result<serde_json::Value, crate::Error>,
742 ) -> Result<serde_json::Value, crate::Error> {
743 let Some(mode) = self.mode else {
744 return result;
745 };
746
747 if AcpProtocolMode::is_incoming_initialize_response(method) {
748 return self.incoming_initialize_response(mode, accepts_lower_version, result);
749 }
750
751 let value = result?;
752 self.ensure_initialized(method)?;
753 ensure_matching_protocol_version(method, self.active_wire_version(), mode.api)?;
754 Ok(value)
755 }
756
757 #[cfg(all(test, feature = "unstable_protocol_v2"))]
758 pub(crate) fn outgoing_response(
759 &self,
760 method: &str,
761 result: Result<serde_json::Value, crate::Error>,
762 ) -> Result<serde_json::Value, crate::Error> {
763 self.outgoing_response_to(&RawJsonRpcId::Number(0), method, result)
764 }
765
766 pub(crate) fn outgoing_response_to(
767 &self,
768 id: &RawJsonRpcId,
769 method: &str,
770 result: Result<serde_json::Value, crate::Error>,
771 ) -> Result<serde_json::Value, crate::Error> {
772 let Some(mode) = self.mode else {
773 return result;
774 };
775
776 if mode.is_outgoing_initialize_response(method) {
777 match mode.initialization_role {
778 InitializationRole::Initiator => {
779 return result.and_then(|_| {
780 Err(unexpected_initialize_response(mode.initialization_role))
781 });
782 }
783 InitializationRole::Responder if !self.is_pending_incoming_initialize(id) => {
784 return result.and_then(|_| {
785 Err(unexpected_initialize_response(mode.initialization_role))
786 });
787 }
788 InitializationRole::Unchecked | InitializationRole::Responder => {}
789 }
790 let mut value = match result {
791 Ok(value) => value,
792 Err(error) => {
793 self.fail_initialize();
794 return Err(error);
795 }
796 };
797 let negotiated = self.pending_initialize().or_else(|| {
798 (mode.initialization_role == InitializationRole::Unchecked).then_some(mode.api)
799 });
800 let negotiated = negotiated
801 .ok_or_else(|| unexpected_initialize_response(mode.initialization_role))?;
802 if let Err(error) = ensure_matching_protocol_version(method, mode.api, negotiated)
803 .and_then(|()| set_protocol_version(&mut value, negotiated))
804 .and_then(|()| validate_native_v2_initialize_response(mode, &value))
805 {
806 self.fail_initialize();
807 return Err(error);
808 }
809 self.complete_initialize(negotiated)?;
810 return Ok(value);
811 }
812
813 let value = result?;
814 self.ensure_initialized(method)?;
815 let wire_version = self.active_wire_version();
816
817 ensure_matching_protocol_version(method, mode.api, wire_version)?;
818 Ok(value)
819 }
820
821 fn incoming_initialize_request(
822 &self,
823 mode: AcpProtocolMode,
824 id: &RawJsonRpcId,
825 mut message: UntypedMessage,
826 ) -> Result<UntypedMessage, crate::Error> {
827 let requested = required_protocol_version_from_value(message.params())?;
828 #[cfg(feature = "unstable_protocol_v2")]
839 if mode.api == ProtocolVersionKind::V2 && requested < mode.api.as_protocol_version() {
840 return Err(crate::v2::schema::Error::unsupported_protocol_version(
841 requested,
842 &[mode.api.as_protocol_version()],
843 )
844 .into());
845 }
846 if mode.api == ProtocolVersionKind::V1 && requested > ProtocolVersion::V1 {
847 message.params = serde_json::Value::Object(super::normalize_v1_initialize_request(
848 message.params(),
849 )?);
850 }
851 #[cfg(feature = "unstable_protocol_v2")]
854 if mode.api == ProtocolVersionKind::V2 && requested > mode.api.as_protocol_version() {
855 let serde_json::Value::Object(params) = message.params() else {
856 return Err(invalid_initialize_protocol_version());
857 };
858 message.params = serde_json::Value::Object(
859 super::normalize_newer_v2_initialize_request(params)?,
860 );
861 }
862
863 set_protocol_version(&mut message.params, mode.api)?;
864 validate_native_v2_initialize_request(mode, message.params())?;
865 self.begin_incoming_initialize(mode, id)?;
866 Ok(message)
867 }
868
869 fn incoming_initialize_response(
870 &self,
871 mode: AcpProtocolMode,
872 accepts_lower_version: bool,
873 result: Result<serde_json::Value, crate::Error>,
874 ) -> Result<serde_json::Value, crate::Error> {
875 let mut value = match result {
876 Ok(value) => value,
877 Err(error) => {
878 self.fail_initialize();
879 return Err(error);
880 }
881 };
882 let response = (|| {
883 let pending = self.pending_initialize().or_else(|| {
884 (mode.initialization_role == InitializationRole::Unchecked).then_some(mode.api)
885 });
886 let pending = pending
887 .ok_or_else(|| unexpected_initialize_response(mode.initialization_role))?;
888 let response_version = required_protocol_version_from_value(&value)?;
889 let wire_version = ProtocolVersionKind::from_protocol_version(response_version)
890 .ok_or_else(|| unsupported_protocol_version(response_version, mode.api))?;
891 #[cfg(feature = "unstable_protocol_v2")]
896 if accepts_lower_version
897 && mode.initialization_role == InitializationRole::Initiator
898 && wire_version < mode.api
899 && let Some(response) = super::SelectedInitializeResponse::from_json(
900 wire_version.as_protocol_version(),
901 value.clone(),
902 )
903 {
904 response?;
905 return Ok(InitializeResponseCheck::LowerVersion);
906 }
907 #[cfg(not(feature = "unstable_protocol_v2"))]
908 let _ = accepts_lower_version;
909 if wire_version != mode.api {
910 return Err(required_protocol_version(mode.api, wire_version));
911 }
912 ensure_matching_protocol_version("initialize", pending, wire_version)?;
913 set_protocol_version(&mut value, wire_version)?;
914 validate_native_v2_initialize_response(mode, &value)?;
915 Ok(InitializeResponseCheck::Negotiated(wire_version))
916 })();
917
918 match response {
919 Ok(InitializeResponseCheck::Negotiated(wire_version)) => {
920 self.complete_initialize(wire_version)?;
921 Ok(value)
922 }
923 Ok(InitializeResponseCheck::LowerVersion) => {
926 self.fail_initialize();
927 Ok(value)
928 }
929 Err(error) => {
930 self.fail_initialize();
931 Err(error)
932 }
933 }
934 }
935
936 fn begin_incoming_initialize(
937 &self,
938 mode: AcpProtocolMode,
939 id: &RawJsonRpcId,
940 ) -> Result<(), crate::Error> {
941 match mode.initialization_role {
942 InitializationRole::Initiator => return Err(invalid_initialize_direction()),
943 InitializationRole::Unchecked | InitializationRole::Responder => {}
944 }
945 self.begin_initialize(mode.api, Some(id))
946 }
947
948 fn begin_outgoing_initialize(&self, mode: AcpProtocolMode) -> Result<(), crate::Error> {
949 match mode.initialization_role {
950 InitializationRole::Responder => return Err(invalid_initialize_direction()),
951 InitializationRole::Unchecked | InitializationRole::Initiator => {}
952 }
953 self.begin_initialize(mode.api, None)
954 }
955
956 fn begin_initialize(
957 &self,
958 requested: ProtocolVersionKind,
959 incoming_id: Option<&RawJsonRpcId>,
960 ) -> Result<(), crate::Error> {
961 let mut state = self
962 .state
963 .lock()
964 .expect("protocol compatibility state mutex poisoned");
965 match state.initialization {
966 InitializationState::Unchecked => {
967 state.pending_initialize = Some(requested);
968 Ok(())
969 }
970 InitializationState::Uninitialized => {
971 state.initialization = InitializationState::Initializing;
972 state.pending_initialize = Some(requested);
973 state.incoming_initialize_id = incoming_id.cloned();
974 Ok(())
975 }
976 InitializationState::Initializing => Err(crate::Error::invalid_request()
977 .data("ACP initialization is already in progress on this connection")),
978 InitializationState::Ready if state.repeatable => {
981 state.initialization = InitializationState::Initializing;
982 state.repeatable = false;
983 state.pending_initialize = Some(requested);
984 state.incoming_initialize_id = incoming_id.cloned();
985 Ok(())
986 }
987 InitializationState::Ready => Err(crate::Error::invalid_request().data(
988 "ACP initialization cannot be repeated after any other request; reconnect to initialize again",
989 )),
990 }
991 }
992
993 fn close_repeat_window(&self) {
995 self.state
996 .lock()
997 .expect("protocol compatibility state mutex poisoned")
998 .repeatable = false;
999 }
1000
1001 fn ensure_initialized(&self, method: &str) -> Result<(), crate::Error> {
1002 let state = self
1003 .state
1004 .lock()
1005 .expect("protocol compatibility state mutex poisoned")
1006 .initialization;
1007 match state {
1008 InitializationState::Unchecked | InitializationState::Ready => Ok(()),
1009 InitializationState::Uninitialized | InitializationState::Initializing => {
1010 Err(initialization_required(method))
1011 }
1012 }
1013 }
1014
1015 fn ensure_notification_allowed(&self, method: &str) -> Result<(), crate::Error> {
1016 let initialization = self
1017 .state
1018 .lock()
1019 .expect("protocol compatibility state mutex poisoned")
1020 .initialization;
1021 if initialization == InitializationState::Initializing && method == "$/cancel_request" {
1022 return Ok(());
1023 }
1024 self.ensure_initialized(method)
1025 }
1026
1027 fn complete_initialize(&self, negotiated: ProtocolVersionKind) -> Result<(), crate::Error> {
1028 let mut state = self
1029 .state
1030 .lock()
1031 .expect("protocol compatibility state mutex poisoned");
1032 if state.pending_initialize.is_none()
1033 && state.initialization != InitializationState::Unchecked
1034 {
1035 return Err(unexpected_initialize_response(
1036 self.mode
1037 .expect("protocol initialization requires an ACP mode")
1038 .initialization_role,
1039 ));
1040 }
1041 if !matches!(
1042 state.initialization,
1043 InitializationState::Unchecked | InitializationState::Initializing
1044 ) {
1045 return Err(unexpected_initialize_response(
1046 self.mode
1047 .expect("protocol initialization requires an ACP mode")
1048 .initialization_role,
1049 ));
1050 }
1051 state.pending_initialize = None;
1052 state.incoming_initialize_id = None;
1053 state.negotiated = negotiated;
1054 state.initialized = true;
1055 if state.initialization == InitializationState::Initializing {
1056 state.initialization = InitializationState::Ready;
1057 state.repeatable = true;
1058 }
1059 Ok(())
1060 }
1061
1062 fn fail_initialize(&self) {
1063 let mut state = self
1064 .state
1065 .lock()
1066 .expect("protocol compatibility state mutex poisoned");
1067 state.pending_initialize = None;
1068 state.incoming_initialize_id = None;
1069 if state.initialization == InitializationState::Initializing {
1070 if state.initialized {
1073 state.initialization = InitializationState::Ready;
1074 state.repeatable = true;
1075 } else {
1076 state.initialization = InitializationState::Uninitialized;
1077 }
1078 }
1079 }
1080
1081 fn has_initialized(&self) -> bool {
1082 self.state
1083 .lock()
1084 .expect("protocol compatibility state mutex poisoned")
1085 .initialized
1086 }
1087
1088 fn is_pending_incoming_initialize(&self, id: &RawJsonRpcId) -> bool {
1089 self.state
1090 .lock()
1091 .expect("protocol compatibility state mutex poisoned")
1092 .incoming_initialize_id
1093 .as_ref()
1094 == Some(id)
1095 }
1096
1097 fn active_wire_version(&self) -> ProtocolVersionKind {
1098 let state = self
1099 .state
1100 .lock()
1101 .expect("protocol compatibility state mutex poisoned");
1102 state.pending_initialize.unwrap_or(state.negotiated)
1103 }
1104
1105 fn pending_initialize(&self) -> Option<ProtocolVersionKind> {
1106 self.state
1107 .lock()
1108 .expect("protocol compatibility state mutex poisoned")
1109 .pending_initialize
1110 }
1111 }
1112
1113 fn required_protocol_version_from_value(
1114 value: &serde_json::Value,
1115 ) -> Result<ProtocolVersion, crate::Error> {
1116 let Some(version) = value.get("protocolVersion") else {
1117 return Err(invalid_initialize_protocol_version());
1118 };
1119
1120 serde_json::from_value(version.clone()).map_err(|_| invalid_initialize_protocol_version())
1121 }
1122
1123 fn outgoing_initialize_params(
1124 mode: AcpProtocolMode,
1125 remote_style: RemoteStyle,
1126 message: &mut UntypedMessage,
1127 ) -> Result<Option<&mut serde_json::Value>, crate::Error> {
1128 if successor_encloses_initialize(message) {
1129 return Err(invalid_prewrapped_initialize());
1130 }
1131
1132 match mode.initialize_surface {
1133 InitializeSurface::Peer => {
1134 Ok((message.method() == "initialize").then_some(&mut message.params))
1135 }
1136 InitializeSurface::Proxy => {
1137 if message.method() == "initialize" {
1138 if remote_style != RemoteStyle::Successor {
1139 return Err(invalid_proxy_initialize_direction());
1140 }
1141 return Ok(Some(&mut message.params));
1142 }
1143 if message.method() == "proxy/initialize" {
1144 return Err(invalid_proxy_initialize_direction());
1145 }
1146 Ok(None)
1147 }
1148 }
1149 }
1150
1151 fn successor_encloses_initialize(message: &UntypedMessage) -> bool {
1152 let mut method = message.method();
1153 let mut params = message.params();
1154 let mut wrapped = false;
1155
1156 while method == "proxy/successor" {
1157 wrapped = true;
1158 let Some(inner_method) = params.get("method").and_then(serde_json::Value::as_str)
1159 else {
1160 return false;
1161 };
1162 method = inner_method;
1163 if method == "proxy/successor" {
1164 let Some(inner_params) = params.get("params") else {
1165 return false;
1166 };
1167 params = inner_params;
1168 }
1169 }
1170
1171 wrapped && matches!(method, "initialize" | "proxy/initialize")
1172 }
1173
1174 fn invalid_initialize_protocol_version() -> crate::Error {
1175 crate::Error::invalid_params()
1176 .data("initialize.protocolVersion must be a valid ACP protocol version")
1177 }
1178
1179 fn initialization_required(method: &str) -> crate::Error {
1180 crate::Error::invalid_request().data(format!(
1181 "ACP initialization must complete before `{method}` can be used",
1182 ))
1183 }
1184
1185 fn invalid_initialize_direction() -> crate::Error {
1186 crate::Error::invalid_request()
1187 .data("ACP clients send `initialize` requests and ACP agents respond to them")
1188 }
1189
1190 fn unexpected_initialize_response(role: InitializationRole) -> crate::Error {
1191 let detail = match role {
1192 InitializationRole::Initiator => "before an initialize request is pending",
1193 InitializationRole::Responder => "before an initialize request was received",
1194 InitializationRole::Unchecked => "without a pending initialize request",
1195 };
1196 crate::Error::invalid_request().data(format!("received an initialize response {detail}"))
1197 }
1198
1199 fn invalid_proxy_initialize_direction() -> crate::Error {
1200 crate::Error::invalid_request().data(
1201 "proxy initialization must arrive as `proxy/initialize`; outgoing `initialize` must target the successor so the connection can apply `proxy/successor`",
1202 )
1203 }
1204
1205 fn invalid_prewrapped_initialize() -> crate::Error {
1206 crate::Error::invalid_request().data(
1207 "initialize requests must be sent as a logical `initialize` message; `proxy/successor` wrapping is applied by connection routing",
1208 )
1209 }
1210
1211 fn set_protocol_version(
1212 value: &mut serde_json::Value,
1213 version: ProtocolVersionKind,
1214 ) -> Result<(), crate::Error> {
1215 let serde_json::Value::Object(object) = value else {
1216 return Err(invalid_initialize_protocol_version());
1217 };
1218 object.insert(
1219 "protocolVersion".into(),
1220 serde_json::to_value(version.as_protocol_version())
1221 .map_err(crate::Error::into_internal_error)?,
1222 );
1223 Ok(())
1224 }
1225
1226 #[cfg_attr(
1228 not(feature = "unstable_protocol_v2"),
1229 allow(clippy::unnecessary_wraps)
1230 )]
1231 fn validate_native_v2_initialize_request(
1232 mode: AcpProtocolMode,
1233 value: &serde_json::Value,
1234 ) -> Result<(), crate::Error> {
1235 #[cfg(feature = "unstable_protocol_v2")]
1236 if mode.initialization_role != InitializationRole::Unchecked {
1237 <crate::v2::schema::InitializeRequest as crate::JsonRpcMessage>::parse_message(
1238 "initialize",
1239 value,
1240 )?;
1241 }
1242 #[cfg(not(feature = "unstable_protocol_v2"))]
1243 let _ = (mode, value);
1244 Ok(())
1245 }
1246
1247 #[cfg_attr(
1248 not(feature = "unstable_protocol_v2"),
1249 allow(clippy::unnecessary_wraps)
1250 )]
1251 fn validate_native_v2_initialize_response(
1252 mode: AcpProtocolMode,
1253 value: &serde_json::Value,
1254 ) -> Result<(), crate::Error> {
1255 #[cfg(feature = "unstable_protocol_v2")]
1256 if mode.initialization_role != InitializationRole::Unchecked {
1257 <crate::v2::schema::InitializeResponse as crate::JsonRpcResponse>::from_json(
1258 "initialize",
1259 value.clone(),
1260 )?;
1261 }
1262 #[cfg(not(feature = "unstable_protocol_v2"))]
1263 let _ = (mode, value);
1264 Ok(())
1265 }
1266
1267 fn ensure_matching_protocol_version(
1268 method: &str,
1269 from: ProtocolVersionKind,
1270 to: ProtocolVersionKind,
1271 ) -> Result<(), crate::Error> {
1272 if from == to {
1273 return Ok(());
1274 }
1275
1276 Err(crate::Error::invalid_request().data(format!(
1277 "ACP protocol translation from {} to {} is not supported for `{method}`; register a handler for the negotiated protocol version",
1278 from.as_protocol_version(),
1279 to.as_protocol_version(),
1280 )))
1281 }
1282
1283 fn unsupported_protocol_version(
1284 version: ProtocolVersion,
1285 supported: ProtocolVersionKind,
1286 ) -> crate::Error {
1287 crate::Error::invalid_request().data(format!(
1288 "unsupported ACP protocol version {version}; this endpoint only supports ACP protocol version {}",
1289 supported.as_protocol_version(),
1290 ))
1291 }
1292
1293 fn required_protocol_version(
1294 required: ProtocolVersionKind,
1295 negotiated: ProtocolVersionKind,
1296 ) -> crate::Error {
1297 crate::Error::invalid_request().data(format!(
1298 "required ACP protocol version {} but peer negotiated {}; use a matching implementation for the negotiated protocol version",
1299 required.as_protocol_version(),
1300 negotiated.as_protocol_version(),
1301 ))
1302 }
1303
1304 #[cfg(all(test, feature = "unstable_protocol_v2"))]
1305 mod tests {
1306 use super::*;
1307 use agent_client_protocol_schema::v2;
1308
1309 fn negotiated(compat: &ProtocolCompat) -> ProtocolVersionKind {
1310 compat
1311 .state
1312 .lock()
1313 .expect("protocol compatibility state mutex poisoned")
1314 .negotiated
1315 }
1316
1317 fn pending_initialize(compat: &ProtocolCompat) -> Option<ProtocolVersionKind> {
1318 compat
1319 .state
1320 .lock()
1321 .expect("protocol compatibility state mutex poisoned")
1322 .pending_initialize
1323 }
1324
1325 fn initialization_state(compat: &ProtocolCompat) -> InitializationState {
1326 compat
1327 .state
1328 .lock()
1329 .expect("protocol compatibility state mutex poisoned")
1330 .initialization
1331 }
1332
1333 fn v2_implementation() -> v2::Implementation {
1334 v2::Implementation::new("protocol-compat-test", env!("CARGO_PKG_VERSION"))
1335 }
1336
1337 fn v2_initialize_request(protocol_version: ProtocolVersion) -> v2::InitializeRequest {
1338 v2::InitializeRequest::new(protocol_version, v2_implementation())
1339 }
1340
1341 fn v2_initialize_response(protocol_version: ProtocolVersion) -> v2::InitializeResponse {
1342 v2::InitializeResponse::new(protocol_version, v2_implementation())
1343 }
1344
1345 #[test]
1346 fn initialize_request_sets_active_wire_version_before_response() -> Result<(), crate::Error>
1347 {
1348 let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
1349 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1350 assert_eq!(
1351 initialization_state(&compat),
1352 InitializationState::Uninitialized
1353 );
1354
1355 compat.incoming_message(UntypedMessage::new(
1356 "initialize",
1357 v2_initialize_request(ProtocolVersion::V2),
1358 )?)?;
1359
1360 assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1361 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1362 assert_eq!(
1363 initialization_state(&compat),
1364 InitializationState::Initializing
1365 );
1366
1367 compat.outgoing_response(
1368 "initialize",
1369 Ok(serde_json::to_value(v2_initialize_response(
1370 ProtocolVersion::V2,
1371 ))?),
1372 )?;
1373
1374 assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1375 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1376 assert_eq!(initialization_state(&compat), InitializationState::Ready);
1377 Ok(())
1378 }
1379
1380 #[test]
1381 fn outgoing_initialize_sets_active_wire_version_before_response() -> Result<(), crate::Error>
1382 {
1383 let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1384 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1385 assert_eq!(
1386 initialization_state(&compat),
1387 InitializationState::Uninitialized
1388 );
1389
1390 compat.outgoing_message(
1391 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V1))?,
1392 RemoteStyle::Counterpart,
1393 )?;
1394
1395 assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1396 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1397 assert_eq!(
1398 initialization_state(&compat),
1399 InitializationState::Initializing
1400 );
1401
1402 compat.incoming_response(
1403 "initialize",
1404 false,
1405 Ok(serde_json::to_value(v2_initialize_response(
1406 ProtocolVersion::V2,
1407 ))?),
1408 )?;
1409
1410 assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1411 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1412 assert_eq!(initialization_state(&compat), InitializationState::Ready);
1413 Ok(())
1414 }
1415
1416 #[test]
1417 fn failed_incoming_initialize_response_clears_pending_wire_version()
1418 -> Result<(), crate::Error> {
1419 let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1420 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1421
1422 compat.outgoing_message(
1423 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V1))?,
1424 RemoteStyle::Counterpart,
1425 )?;
1426
1427 assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1428 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1429
1430 let result = compat.incoming_response(
1431 "initialize",
1432 false,
1433 Err(crate::Error::invalid_request().data("initialize failed")),
1434 );
1435
1436 assert!(result.is_err());
1437 assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1438 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1439 assert_eq!(
1440 initialization_state(&compat),
1441 InitializationState::Uninitialized
1442 );
1443 compat.outgoing_message(
1444 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1445 RemoteStyle::Counterpart,
1446 )?;
1447 assert_eq!(
1448 initialization_state(&compat),
1449 InitializationState::Initializing
1450 );
1451 Ok(())
1452 }
1453
1454 #[test]
1455 fn incoming_initialize_response_requires_protocol_version() -> Result<(), crate::Error> {
1456 for value in [
1457 serde_json::json!({}),
1458 serde_json::json!({ "protocolVersion": 100_000 }),
1459 ] {
1460 let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1461 compat.outgoing_message(
1462 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V1))?,
1463 RemoteStyle::Counterpart,
1464 )?;
1465
1466 let error = compat
1467 .incoming_response("initialize", false, Ok(value))
1468 .expect_err("initialize responses must declare an ACP protocol version");
1469 let data = error
1470 .data
1471 .as_ref()
1472 .and_then(|data| data.as_str())
1473 .unwrap_or_default();
1474 assert!(data.contains("protocolVersion"), "{error:?}");
1475 assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
1476 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1477 assert_eq!(
1478 initialization_state(&compat),
1479 InitializationState::Uninitialized
1480 );
1481 }
1482
1483 Ok(())
1484 }
1485
1486 fn v2_client_receiving(
1489 negotiation: bool,
1490 response: Result<serde_json::Value, crate::Error>,
1491 ) -> Result<(ProtocolCompat, Result<serde_json::Value, crate::Error>), crate::Error>
1492 {
1493 let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1494 compat.outgoing_message(
1495 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1496 RemoteStyle::Counterpart,
1497 )?;
1498 let result = compat.incoming_response("initialize", negotiation, response);
1499 Ok((compat, result))
1500 }
1501
1502 fn v1_initialize_response() -> crate::v1::schema::InitializeResponse {
1503 crate::v1::schema::InitializeResponse::new(ProtocolVersion::V1)
1504 .agent_info(crate::v1::schema::Implementation::new("v1-agent", "1.0.0"))
1505 }
1506
1507 #[test]
1508 fn plain_initialize_request_rejects_lower_version() -> Result<(), crate::Error> {
1509 let (compat, result) =
1510 v2_client_receiving(false, Ok(serde_json::to_value(v1_initialize_response())?))?;
1511 let error = result.expect_err("send_request keeps the local version guard");
1512 let data = error
1513 .data
1514 .as_ref()
1515 .and_then(|data| data.as_str())
1516 .unwrap_or_default();
1517 assert!(data.contains("peer negotiated 1"), "{error:?}");
1518 assert_eq!(
1519 initialization_state(&compat),
1520 InitializationState::Uninitialized
1521 );
1522 Ok(())
1523 }
1524
1525 #[test]
1526 fn initialize_negotiation_receives_lower_version_as_success() -> Result<(), crate::Error> {
1527 let v1_response = serde_json::to_value(v1_initialize_response())?;
1528 let (compat, result) = v2_client_receiving(true, Ok(v1_response.clone()))?;
1529 assert_eq!(result?, v1_response);
1530
1531 assert_eq!(
1533 initialization_state(&compat),
1534 InitializationState::Uninitialized
1535 );
1536 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
1537 compat
1538 .outgoing_message(
1539 UntypedMessage::new("session/new", serde_json::json!({}))?,
1540 RemoteStyle::Counterpart,
1541 )
1542 .expect_err("v2 traffic still requires a v2 handshake");
1543 compat.outgoing_message(
1544 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1545 RemoteStyle::Counterpart,
1546 )?;
1547 Ok(())
1548 }
1549
1550 #[test]
1551 fn initialize_negotiation_keeps_failures_as_errors() -> Result<(), crate::Error> {
1552 let remote: crate::Error = crate::v2::schema::Error::unsupported_protocol_version(
1553 ProtocolVersion::V2,
1554 &[ProtocolVersion::V1],
1555 )
1556 .into();
1557 let (compat, result) = v2_client_receiving(true, Err(remote.clone()))?;
1558 assert_eq!(result, Err(remote));
1559 assert_eq!(
1560 initialization_state(&compat),
1561 InitializationState::Uninitialized
1562 );
1563
1564 for malformed in [
1565 serde_json::json!({}),
1566 serde_json::json!({ "protocolVersion": "one" }),
1567 serde_json::json!({ "protocolVersion": 0 }),
1568 ] {
1569 let (compat, result) = v2_client_receiving(true, Ok(malformed))?;
1570 result.expect_err("malformed initialize responses are errors");
1571 assert_eq!(
1572 initialization_state(&compat),
1573 InitializationState::Uninitialized
1574 );
1575 }
1576 Ok(())
1577 }
1578
1579 #[test]
1580 fn initialize_negotiation_does_not_change_proxies() -> Result<(), crate::Error> {
1581 let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
1582 compat.outgoing_message(
1583 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1584 RemoteStyle::Successor,
1585 )?;
1586 compat
1587 .incoming_response(
1588 "initialize",
1589 true,
1590 Ok(serde_json::to_value(v1_initialize_response())?),
1591 )
1592 .expect_err("only a client's own handshake can report a lower version");
1593 Ok(())
1594 }
1595
1596 #[test]
1597 fn initialize_outcome_parses_by_selected_version() -> Result<(), crate::Error> {
1598 use crate::JsonRpcResponse as _;
1599
1600 let v2_response = v2_initialize_response(ProtocolVersion::V2);
1601 assert_eq!(
1602 super::super::InitializeOutcome::from_json(
1603 "initialize",
1604 serde_json::to_value(&v2_response)?
1605 )?,
1606 super::super::InitializeOutcome::Initialized(v2_response)
1607 );
1608
1609 let v1_response = v1_initialize_response();
1610 let outcome = super::super::InitializeOutcome::from_json(
1611 "initialize",
1612 serde_json::to_value(&v1_response)?,
1613 )?;
1614 assert_eq!(
1615 outcome,
1616 super::super::InitializeOutcome::LowerVersion(
1617 super::super::InitializationVersionMismatch::new(
1618 ProtocolVersion::V2,
1619 super::super::SelectedInitializeResponse::V1(v1_response),
1620 )
1621 )
1622 );
1623 let super::super::InitializeOutcome::LowerVersion(mismatch) = &outcome else {
1624 unreachable!();
1625 };
1626 assert_eq!(mismatch.requested, ProtocolVersion::V2);
1627 assert_eq!(mismatch.selected(), ProtocolVersion::V1);
1628
1629 for value in [
1630 serde_json::json!({}),
1631 serde_json::json!({ "protocolVersion": 3 }),
1632 ] {
1633 super::super::InitializeOutcome::from_json("initialize", value)
1634 .expect_err("only v2 and lower known versions are outcomes");
1635 }
1636 Ok(())
1637 }
1638
1639 #[test]
1640 fn malformed_v2_initialize_requests_do_not_begin_a_handshake() -> Result<(), crate::Error> {
1641 let malformed = serde_json::json!({ "protocolVersion": ProtocolVersion::V2 });
1642
1643 let client = ProtocolCompat::new(ProtocolMode::v2_client());
1644 client
1645 .outgoing_message(
1646 UntypedMessage::new("initialize", malformed.clone())?,
1647 RemoteStyle::Counterpart,
1648 )
1649 .expect_err("native v2 clients must send the complete initialize request shape");
1650 assert_eq!(
1651 initialization_state(&client),
1652 InitializationState::Uninitialized
1653 );
1654 client.outgoing_message(
1655 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1656 RemoteStyle::Counterpart,
1657 )?;
1658 assert_eq!(
1659 initialization_state(&client),
1660 InitializationState::Initializing
1661 );
1662
1663 let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1664 agent
1665 .incoming_message(UntypedMessage::new("initialize", malformed)?)
1666 .expect_err("native v2 agents must receive the complete initialize request shape");
1667 assert_eq!(
1668 initialization_state(&agent),
1669 InitializationState::Uninitialized
1670 );
1671 agent.incoming_message(UntypedMessage::new(
1672 "initialize",
1673 v2_initialize_request(ProtocolVersion::V2),
1674 )?)?;
1675 assert_eq!(
1676 initialization_state(&agent),
1677 InitializationState::Initializing
1678 );
1679 Ok(())
1680 }
1681
1682 #[test]
1683 fn malformed_v2_initialize_success_leaves_client_uninitialized_for_retry()
1684 -> Result<(), crate::Error> {
1685 let compat = ProtocolCompat::new(ProtocolMode::v2_client());
1686 compat.outgoing_message(
1687 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1688 RemoteStyle::Counterpart,
1689 )?;
1690
1691 compat
1692 .incoming_response(
1693 "initialize",
1694 false,
1695 Ok(serde_json::json!({ "protocolVersion": ProtocolVersion::V2 })),
1696 )
1697 .expect_err("initialize success must contain the complete v2 response shape");
1698 assert_eq!(
1699 initialization_state(&compat),
1700 InitializationState::Uninitialized
1701 );
1702 assert_eq!(pending_initialize(&compat), None);
1703
1704 compat.outgoing_message(
1705 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1706 RemoteStyle::Counterpart,
1707 )?;
1708 assert_eq!(
1709 initialization_state(&compat),
1710 InitializationState::Initializing
1711 );
1712 Ok(())
1713 }
1714
1715 #[test]
1716 fn malformed_v2_initialize_success_leaves_agent_uninitialized_for_retry()
1717 -> Result<(), crate::Error> {
1718 let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
1719 compat.incoming_message(UntypedMessage::new(
1720 "initialize",
1721 v2_initialize_request(ProtocolVersion::V2),
1722 )?)?;
1723
1724 compat
1725 .outgoing_response(
1726 "initialize",
1727 Ok(serde_json::json!({ "protocolVersion": ProtocolVersion::V2 })),
1728 )
1729 .expect_err("initialize success must contain the complete v2 response shape");
1730 assert_eq!(
1731 initialization_state(&compat),
1732 InitializationState::Uninitialized
1733 );
1734 assert_eq!(pending_initialize(&compat), None);
1735
1736 compat.incoming_message(UntypedMessage::new(
1737 "initialize",
1738 v2_initialize_request(ProtocolVersion::V2),
1739 )?)?;
1740 assert_eq!(
1741 initialization_state(&compat),
1742 InitializationState::Initializing
1743 );
1744 Ok(())
1745 }
1746
1747 #[test]
1748 fn v2_peer_traffic_requires_completed_initialization() -> Result<(), crate::Error> {
1749 for compat in [
1750 ProtocolCompat::new(ProtocolMode::v2_agent()),
1751 ProtocolCompat::new(ProtocolMode::v2_client()),
1752 ] {
1753 for error in [
1754 compat
1755 .incoming_message(UntypedMessage::new(
1756 "session/new",
1757 serde_json::json!({}),
1758 )?)
1759 .expect_err("incoming requests must wait for initialization"),
1760 compat
1761 .outgoing_message(
1762 UntypedMessage::new("session/new", serde_json::json!({}))?,
1763 RemoteStyle::Counterpart,
1764 )
1765 .expect_err("outgoing requests must wait for initialization"),
1766 compat
1767 .incoming_notification(UntypedMessage::new(
1768 "session/update",
1769 serde_json::json!({}),
1770 )?)
1771 .expect_err("incoming notifications must wait for initialization"),
1772 compat
1773 .outgoing_notification(UntypedMessage::new(
1774 "session/update",
1775 serde_json::json!({}),
1776 )?)
1777 .expect_err("outgoing notifications must wait for initialization"),
1778 compat
1779 .incoming_response("session/new", false, Ok(serde_json::json!({})))
1780 .expect_err("incoming responses must wait for initialization"),
1781 compat
1782 .outgoing_response("session/new", Ok(serde_json::json!({})))
1783 .expect_err("outgoing responses must wait for initialization"),
1784 ] {
1785 let data = error
1786 .data
1787 .as_ref()
1788 .and_then(|data| data.as_str())
1789 .unwrap_or_default();
1790 assert!(data.contains("initialization must complete"), "{error:?}");
1791 }
1792 }
1793 Ok(())
1794 }
1795
1796 #[test]
1797 fn v2_initialization_allows_only_protocol_cancellation_notifications()
1798 -> Result<(), crate::Error> {
1799 let client = ProtocolCompat::new(ProtocolMode::v2_client());
1800 client.outgoing_message(
1801 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1802 RemoteStyle::Counterpart,
1803 )?;
1804 client.outgoing_notification(UntypedMessage::new(
1805 "$/cancel_request",
1806 serde_json::json!({ "requestId": 1 }),
1807 )?)?;
1808 client
1809 .outgoing_notification(UntypedMessage::new(
1810 "session/update",
1811 serde_json::json!({}),
1812 )?)
1813 .expect_err("ordinary notifications must wait for initialization");
1814
1815 let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1816 agent.incoming_message(UntypedMessage::new(
1817 "initialize",
1818 v2_initialize_request(ProtocolVersion::V2),
1819 )?)?;
1820 agent.incoming_notification(UntypedMessage::new(
1821 "$/cancel_request",
1822 serde_json::json!({ "requestId": 1 }),
1823 )?)?;
1824 agent
1825 .incoming_notification(UntypedMessage::new(
1826 "session/update",
1827 serde_json::json!({}),
1828 )?)
1829 .expect_err("ordinary notifications must wait for initialization");
1830 Ok(())
1831 }
1832
1833 fn error_data(error: &crate::Error) -> &str {
1834 error
1835 .data
1836 .as_ref()
1837 .and_then(|data| data.as_str())
1838 .unwrap_or_default()
1839 }
1840
1841 #[test]
1842 fn v2_peer_initialization_has_one_direction() -> Result<(), crate::Error> {
1843 let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1844 let error = agent
1845 .outgoing_message(
1846 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1847 RemoteStyle::Counterpart,
1848 )
1849 .expect_err("agents must not initiate initialization");
1850 assert!(
1851 error_data(&error).contains("clients send `initialize`"),
1852 "{error:?}"
1853 );
1854 assert_eq!(
1855 initialization_state(&agent),
1856 InitializationState::Uninitialized
1857 );
1858
1859 let client = ProtocolCompat::new(ProtocolMode::v2_client());
1860 let error = client
1861 .incoming_message(UntypedMessage::new(
1862 "initialize",
1863 v2_initialize_request(ProtocolVersion::V2),
1864 )?)
1865 .expect_err("clients must not receive initialization requests");
1866 assert!(
1867 error_data(&error).contains("clients send `initialize`"),
1868 "{error:?}"
1869 );
1870 assert_eq!(
1871 initialization_state(&client),
1872 InitializationState::Uninitialized
1873 );
1874 Ok(())
1875 }
1876
1877 fn initialized_v2_agent() -> Result<ProtocolCompat, crate::Error> {
1879 let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1880 agent.incoming_message(UntypedMessage::new(
1881 "initialize",
1882 v2_initialize_request(ProtocolVersion::V2),
1883 )?)?;
1884 agent.outgoing_response(
1885 "initialize",
1886 Ok(serde_json::to_value(v2_initialize_response(
1887 ProtocolVersion::V2,
1888 ))?),
1889 )?;
1890 Ok(agent)
1891 }
1892
1893 fn initialized_v2_client() -> Result<ProtocolCompat, crate::Error> {
1895 let client = ProtocolCompat::new(ProtocolMode::v2_client());
1896 client.outgoing_message(
1897 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
1898 RemoteStyle::Counterpart,
1899 )?;
1900 client.incoming_response(
1901 "initialize",
1902 false,
1903 Ok(serde_json::to_value(v2_initialize_response(
1904 ProtocolVersion::V2,
1905 ))?),
1906 )?;
1907 Ok(client)
1908 }
1909
1910 #[test]
1911 fn v2_agent_accepts_repeated_initialize_before_other_requests() -> Result<(), crate::Error>
1912 {
1913 let agent = initialized_v2_agent()?;
1914 assert_eq!(initialization_state(&agent), InitializationState::Ready);
1915
1916 agent.incoming_notification(UntypedMessage::new(
1918 "$/cancel_request",
1919 serde_json::json!({ "requestId": 1 }),
1920 )?)?;
1921
1922 for _ in 0..2 {
1923 agent.incoming_message(UntypedMessage::new(
1924 "initialize",
1925 v2_initialize_request(ProtocolVersion::V2),
1926 )?)?;
1927 assert_eq!(
1928 initialization_state(&agent),
1929 InitializationState::Initializing
1930 );
1931 agent
1932 .incoming_message(UntypedMessage::new("session/new", serde_json::json!({}))?)
1933 .expect_err("requests must wait for the repeated initialize");
1934 agent.outgoing_response(
1935 "initialize",
1936 Ok(serde_json::to_value(v2_initialize_response(
1937 ProtocolVersion::V2,
1938 ))?),
1939 )?;
1940 assert_eq!(initialization_state(&agent), InitializationState::Ready);
1941 assert_eq!(negotiated(&agent), ProtocolVersionKind::V2);
1942 }
1943
1944 agent.incoming_message(UntypedMessage::new("session/new", serde_json::json!({}))?)?;
1945 let error = agent
1946 .incoming_message(UntypedMessage::new(
1947 "initialize",
1948 v2_initialize_request(ProtocolVersion::V2),
1949 )?)
1950 .expect_err("initialize must not be repeated after another request");
1951 assert_eq!(i32::from(error.code), -32600);
1952 assert!(
1953 error_data(&error).contains("cannot be repeated after any other request"),
1954 "{error:?}"
1955 );
1956 assert_eq!(initialization_state(&agent), InitializationState::Ready);
1957 Ok(())
1958 }
1959
1960 #[test]
1961 fn v2_agent_keeps_window_open_when_a_request_is_rejected() -> Result<(), crate::Error> {
1962 let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
1963 agent.incoming_message(UntypedMessage::new(
1964 "initialize",
1965 v2_initialize_request(ProtocolVersion::V2),
1966 )?)?;
1967 agent
1969 .incoming_message(UntypedMessage::new("session/new", serde_json::json!({}))?)
1970 .expect_err("requests must wait for initialization");
1971 agent.outgoing_response(
1972 "initialize",
1973 Ok(serde_json::to_value(v2_initialize_response(
1974 ProtocolVersion::V2,
1975 ))?),
1976 )?;
1977 agent.incoming_message(UntypedMessage::new(
1978 "initialize",
1979 v2_initialize_request(ProtocolVersion::V2),
1980 )?)?;
1981 assert_eq!(
1982 initialization_state(&agent),
1983 InitializationState::Initializing
1984 );
1985 Ok(())
1986 }
1987
1988 #[test]
1989 fn failed_repeated_initialize_keeps_previous_initialization() -> Result<(), crate::Error> {
1990 let agent = initialized_v2_agent()?;
1991 agent.incoming_message(UntypedMessage::new(
1992 "initialize",
1993 v2_initialize_request(ProtocolVersion::V2),
1994 )?)?;
1995 agent
1996 .outgoing_response(
1997 "initialize",
1998 Err(crate::Error::invalid_params().data("rejected")),
1999 )
2000 .expect_err("the handler's error is forwarded");
2001 assert_eq!(initialization_state(&agent), InitializationState::Ready);
2002
2003 agent
2005 .incoming_message(UntypedMessage::new(
2006 "initialize",
2007 serde_json::json!({ "protocolVersion": 2 }),
2008 )?)
2009 .expect_err("an exact v2 repeat must be a valid v2 request");
2010 assert_eq!(initialization_state(&agent), InitializationState::Ready);
2011
2012 agent.incoming_message(UntypedMessage::new(
2014 "initialize",
2015 v2_initialize_request(ProtocolVersion::V2),
2016 )?)?;
2017 agent.outgoing_response(
2018 "initialize",
2019 Ok(serde_json::to_value(v2_initialize_response(
2020 ProtocolVersion::V2,
2021 ))?),
2022 )?;
2023 agent.incoming_message(UntypedMessage::new("session/new", serde_json::json!({}))?)?;
2024 Ok(())
2025 }
2026
2027 #[test]
2028 fn v2_agent_answers_lower_version_repeat_like_a_first_initialize()
2029 -> Result<(), crate::Error> {
2030 let agent = initialized_v2_agent()?;
2031 let error = agent
2032 .incoming_message(UntypedMessage::new(
2033 "initialize",
2034 serde_json::json!({ "protocolVersion": 1, "clientCapabilities": {} }),
2035 )?)
2036 .expect_err("a v2 agent supports no version at or below v1");
2037 assert_eq!(i32::from(error.code), -32022);
2040 assert_eq!(
2041 error.data,
2042 Some(serde_json::json!({ "requested": 1, "supported": [2] }))
2043 );
2044 assert_eq!(initialization_state(&agent), InitializationState::Ready);
2046 assert_eq!(agent.active_wire_version(), ProtocolVersionKind::V2);
2047 agent.incoming_message(UntypedMessage::new(
2048 "initialize",
2049 v2_initialize_request(ProtocolVersion::V2),
2050 )?)?;
2051 Ok(())
2052 }
2053
2054 #[test]
2055 fn v2_client_may_repeat_initialize_before_other_requests() -> Result<(), crate::Error> {
2056 let client = initialized_v2_client()?;
2057 client.outgoing_message(
2058 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2059 RemoteStyle::Counterpart,
2060 )?;
2061 assert_eq!(
2062 initialization_state(&client),
2063 InitializationState::Initializing
2064 );
2065 client.incoming_response(
2066 "initialize",
2067 false,
2068 Ok(serde_json::to_value(v2_initialize_response(
2069 ProtocolVersion::V2,
2070 ))?),
2071 )?;
2072 assert_eq!(initialization_state(&client), InitializationState::Ready);
2073
2074 client.incoming_message(UntypedMessage::new(
2076 "session/request_permission",
2077 serde_json::json!({}),
2078 )?)?;
2079 client.outgoing_message(
2080 UntypedMessage::new("session/new", serde_json::json!({}))?,
2081 RemoteStyle::Counterpart,
2082 )?;
2083 let error = client
2084 .outgoing_message(
2085 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2086 RemoteStyle::Counterpart,
2087 )
2088 .expect_err("initialize must not be repeated after another request");
2089 assert!(
2090 error_data(&error).contains("cannot be repeated after any other request"),
2091 "{error:?}"
2092 );
2093 assert_eq!(initialization_state(&client), InitializationState::Ready);
2094 Ok(())
2095 }
2096
2097 #[test]
2098 fn concurrent_initialize_is_rejected_for_first_and_repeated_handshakes()
2099 -> Result<(), crate::Error> {
2100 for agent in [
2101 ProtocolCompat::new(ProtocolMode::v2_agent()),
2102 initialized_v2_agent()?,
2103 ] {
2104 agent.incoming_request(
2105 &RawJsonRpcId::Number(1),
2106 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2107 )?;
2108 let error = agent
2109 .incoming_request(
2110 &RawJsonRpcId::Number(2),
2111 UntypedMessage::new(
2112 "initialize",
2113 v2_initialize_request(ProtocolVersion::V2),
2114 )?,
2115 )
2116 .expect_err("only one initialize may be in flight");
2117 assert_eq!(i32::from(error.code), -32600);
2118 assert!(error_data(&error).contains("in progress"), "{error:?}");
2119 }
2120 Ok(())
2121 }
2122
2123 #[test]
2124 fn v2_connections_reject_null_request_ids() -> Result<(), crate::Error> {
2125 for compat in [
2126 ProtocolCompat::new(ProtocolMode::v2_agent()),
2127 ProtocolCompat::new(ProtocolMode::v2_proxy()),
2128 initialized_v2_agent()?,
2129 initialized_v2_client()?,
2130 ] {
2131 for method in ["initialize", "proxy/initialize", "session/new"] {
2132 let state = initialization_state(&compat);
2133 let error = compat
2134 .incoming_request(
2135 &RawJsonRpcId::Null,
2136 UntypedMessage::new(
2137 method,
2138 v2_initialize_request(ProtocolVersion::V2),
2139 )?,
2140 )
2141 .expect_err("v2 request ids must not be null");
2142 assert_eq!(i32::from(error.code), -32600);
2143 assert!(error_data(&error).contains("not null"), "{error:?}");
2144 assert_eq!(initialization_state(&compat), state);
2145 assert_eq!(pending_initialize(&compat), None);
2146 }
2147 }
2148
2149 let v1_agent = ProtocolCompat::new(ProtocolMode::v1_agent());
2151 v1_agent.incoming_request(
2152 &RawJsonRpcId::Null,
2153 UntypedMessage::new(
2154 "initialize",
2155 serde_json::json!({ "protocolVersion": 1, "clientCapabilities": {} }),
2156 )?,
2157 )?;
2158 Ok(())
2159 }
2160
2161 #[test]
2162 fn newer_version_initialize_keeps_what_parses_under_v2() -> Result<(), crate::Error> {
2163 let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
2164 let request = agent.incoming_message(UntypedMessage::new(
2165 "initialize",
2166 serde_json::json!({
2167 "protocolVersion": 3,
2168 "info": { "name": "future-client" },
2169 "capabilities": {
2170 "auth": "malformed",
2171 "elicitation": {},
2172 "_futureCapability": { "dropped": true }
2173 },
2174 "_meta": "malformed",
2175 "_futureField": true
2176 }),
2177 )?)?;
2178 let initialize: v2::InitializeRequest =
2179 serde_json::from_value(request.params().clone())?;
2180 assert_eq!(initialize.protocol_version, ProtocolVersion::V2);
2181 assert_eq!(
2182 initialize.info,
2183 v2::Implementation::new("unknown", "unknown")
2184 );
2185 assert_eq!(initialize.capabilities.auth, None);
2186 assert!(initialize.capabilities.elicitation.is_some());
2187 assert_eq!(initialize.meta, None);
2188 assert_eq!(request.params(), &serde_json::to_value(&initialize)?);
2189
2190 let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
2193 let request = agent.incoming_message(UntypedMessage::new(
2194 "initialize",
2195 serde_json::json!({
2196 "protocolVersion": 3,
2197 "info": { "name": "future-client", "version": "3.0.0" },
2198 "capabilities": [],
2199 "_meta": { "kept": true }
2200 }),
2201 )?)?;
2202 let initialize: v2::InitializeRequest =
2203 serde_json::from_value(request.params().clone())?;
2204 assert_eq!(
2205 initialize.info,
2206 v2::Implementation::new("future-client", "3.0.0")
2207 );
2208 assert_eq!(initialize.capabilities, v2::ClientCapabilities::new());
2209 assert_eq!(
2210 request.params()["_meta"],
2211 serde_json::json!({ "kept": true })
2212 );
2213 Ok(())
2214 }
2215
2216 #[test]
2217 fn exact_version_initialize_stays_strict() -> Result<(), crate::Error> {
2218 let agent = ProtocolCompat::new(ProtocolMode::v2_agent());
2219 let error = agent
2220 .incoming_message(UntypedMessage::new(
2221 "initialize",
2222 serde_json::json!({ "protocolVersion": 2, "info": { "name": "client" } }),
2223 )?)
2224 .expect_err("an exact v2 request must carry a valid `info`");
2225 assert_eq!(i32::from(error.code), -32602);
2226 assert_eq!(
2227 initialization_state(&agent),
2228 InitializationState::Uninitialized
2229 );
2230 Ok(())
2231 }
2232
2233 #[test]
2234 fn v1_downgrade_keeps_what_parses_under_v1() -> Result<(), crate::Error> {
2235 let agent = ProtocolCompat::new(ProtocolMode::v1_agent());
2236 let request = agent.incoming_message(UntypedMessage::new(
2237 "initialize",
2238 serde_json::json!({
2239 "protocolVersion": 2,
2240 "info": "malformed",
2241 "capabilities": { "elicitation": {} },
2242 "_meta": 7
2243 }),
2244 )?)?;
2245 let initialize: crate::v1::schema::InitializeRequest =
2246 serde_json::from_value(request.params().clone())?;
2247 assert_eq!(initialize.protocol_version, ProtocolVersion::V1);
2248 assert_eq!(initialize.client_info, None);
2249 assert!(initialize.client_capabilities.elicitation.is_some());
2250 assert_eq!(initialize.meta, None);
2251 assert!(request.params().get("clientInfo").is_none());
2252 assert!(request.params().get("_meta").is_none());
2253
2254 let agent = ProtocolCompat::new(ProtocolMode::v1_agent());
2255 let request = agent.incoming_message(UntypedMessage::new(
2256 "initialize",
2257 serde_json::json!({ "protocolVersion": 2 }),
2258 )?)?;
2259 let initialize: crate::v1::schema::InitializeRequest =
2260 serde_json::from_value(request.params().clone())?;
2261 assert_eq!(initialize.client_info, None);
2262 Ok(())
2263 }
2264
2265 #[test]
2266 fn rejected_concurrent_initialize_does_not_clear_the_active_handshake()
2267 -> Result<(), crate::Error> {
2268 let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
2269 let accepted_id = RawJsonRpcId::Number(1);
2270 let rejected_id = RawJsonRpcId::Number(2);
2271
2272 compat.incoming_request(
2273 &accepted_id,
2274 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2275 )?;
2276 let duplicate_error = compat
2277 .incoming_request(
2278 &rejected_id,
2279 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2280 )
2281 .expect_err("a second initialize request must be rejected while one is active");
2282 compat
2283 .outgoing_response_to(&rejected_id, "initialize", Err(duplicate_error))
2284 .expect_err("the rejected initialize receives its own error response");
2285
2286 assert_eq!(
2287 initialization_state(&compat),
2288 InitializationState::Initializing
2289 );
2290 assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
2291
2292 compat.outgoing_response_to(
2293 &accepted_id,
2294 "initialize",
2295 Ok(serde_json::to_value(v2_initialize_response(
2296 ProtocolVersion::V2,
2297 ))?),
2298 )?;
2299 assert_eq!(initialization_state(&compat), InitializationState::Ready);
2300 Ok(())
2301 }
2302
2303 #[test]
2304 fn rejected_wrong_direction_initialize_does_not_clear_client_handshake()
2305 -> Result<(), crate::Error> {
2306 let compat = ProtocolCompat::new(ProtocolMode::v2_client());
2307 compat.outgoing_message(
2308 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2309 RemoteStyle::Counterpart,
2310 )?;
2311
2312 let wrong_direction_id = RawJsonRpcId::Number(2);
2313 let wrong_direction_error = compat
2314 .incoming_request(
2315 &wrong_direction_id,
2316 UntypedMessage::new("initialize", v2_initialize_request(ProtocolVersion::V2))?,
2317 )
2318 .expect_err("agents must not send initialize requests to clients");
2319 compat
2320 .outgoing_response_to(
2321 &wrong_direction_id,
2322 "initialize",
2323 Err(wrong_direction_error),
2324 )
2325 .expect_err("the wrong-direction initialize receives its own error response");
2326
2327 assert_eq!(
2328 initialization_state(&compat),
2329 InitializationState::Initializing
2330 );
2331 assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
2332
2333 compat.incoming_response(
2334 "initialize",
2335 false,
2336 Ok(serde_json::to_value(v2_initialize_response(
2337 ProtocolVersion::V2,
2338 ))?),
2339 )?;
2340 assert_eq!(initialization_state(&compat), InitializationState::Ready);
2341 Ok(())
2342 }
2343
2344 #[test]
2345 fn incoming_initialize_request_negotiates_newer_protocol_versions()
2346 -> Result<(), crate::Error> {
2347 for requested in [2_u16, 3, u16::MAX] {
2348 let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
2349 let request = compat.incoming_message(UntypedMessage::new(
2350 "initialize",
2351 v2_initialize_request(ProtocolVersion::from(requested)),
2352 )?)?;
2353 assert_eq!(request.params()["protocolVersion"], serde_json::json!(2));
2354 assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
2355
2356 let response = compat.outgoing_response(
2357 "initialize",
2358 Ok(serde_json::to_value(v2_initialize_response(
2359 ProtocolVersion::V2,
2360 ))?),
2361 )?;
2362 assert_eq!(response["protocolVersion"], serde_json::json!(2));
2363 assert_eq!(initialization_state(&compat), InitializationState::Ready);
2364 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
2365 }
2366 Ok(())
2367 }
2368
2369 #[test]
2370 fn incoming_initialize_request_rejects_unsupported_protocol_version()
2371 -> Result<(), crate::Error> {
2372 let compat = ProtocolCompat::new(ProtocolMode::v2_agent());
2373 let error = compat
2374 .incoming_message(UntypedMessage::new(
2375 "initialize",
2376 serde_json::json!({ "protocolVersion": 1, "clientCapabilities": {} }),
2377 )?)
2378 .expect_err("v2 agents should reject v1 initialization without a v1 handler");
2379 assert_eq!(i32::from(error.code), -32022);
2380 assert_eq!(
2381 error.data,
2382 Some(serde_json::json!({ "requested": 1, "supported": [2] }))
2383 );
2384 assert_eq!(negotiated(&compat), ProtocolVersionKind::V2);
2385 assert_eq!(compat.active_wire_version(), ProtocolVersionKind::V2);
2386 assert_eq!(
2387 initialization_state(&compat),
2388 InitializationState::Uninitialized
2389 );
2390
2391 Ok(())
2392 }
2393
2394 #[test]
2395 fn v1_proxy_negotiates_newer_proxy_initialize_down_to_v1() -> Result<(), crate::Error> {
2396 for requested in [2_u16, 3] {
2397 let compat = ProtocolCompat::new(ProtocolMode::v1_proxy());
2398 let request = compat.incoming_message(UntypedMessage::new(
2399 "proxy/initialize",
2400 serde_json::json!({
2401 "protocolVersion": requested,
2402 "info": { "name": "future-client", "version": "1.0.0" },
2403 "capabilities": { "elicitation": {}, "_future": true },
2404 "_futureField": true
2405 }),
2406 )?)?;
2407 let initialize: crate::v1::schema::InitializeRequest =
2409 serde_json::from_value(request.params().clone())?;
2410 assert_eq!(initialize.protocol_version, ProtocolVersion::V1);
2411 assert_eq!(
2412 initialize.client_info,
2413 Some(crate::v1::schema::Implementation::new(
2414 "future-client",
2415 "1.0.0"
2416 ))
2417 );
2418 assert!(initialize.client_capabilities.elicitation.is_some());
2419 assert!(request.params().get("_futureField").is_none());
2420 assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V1));
2421
2422 let response = compat.outgoing_response(
2423 "proxy/initialize",
2424 Ok(serde_json::to_value(v1_initialize_response())?),
2425 )?;
2426 assert_eq!(response["protocolVersion"], serde_json::json!(1));
2427 }
2428
2429 let compat = ProtocolCompat::new(ProtocolMode::v1_proxy());
2431 let request = compat.incoming_message(UntypedMessage::new(
2432 "proxy/initialize",
2433 serde_json::json!({ "protocolVersion": 0 }),
2434 )?)?;
2435 assert_eq!(request.params()["protocolVersion"], serde_json::json!(1));
2436 Ok(())
2437 }
2438
2439 #[test]
2440 fn v2_proxy_negotiates_proxy_initialize_like_a_v2_agent() -> Result<(), crate::Error> {
2441 let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
2442 let request = compat.incoming_message(UntypedMessage::new(
2443 "proxy/initialize",
2444 serde_json::json!({
2445 "protocolVersion": 3,
2446 "info": { "name": "future-client" },
2447 "capabilities": { "elicitation": {}, "_future": { "dropped": true } },
2448 "_futureField": true
2449 }),
2450 )?)?;
2451 let initialize: v2::InitializeRequest =
2452 serde_json::from_value(request.params().clone())?;
2453 assert_eq!(initialize.protocol_version, ProtocolVersion::V2);
2454 assert_eq!(
2455 initialize.info,
2456 v2::Implementation::new("unknown", "unknown")
2457 );
2458 assert!(initialize.capabilities.elicitation.is_some());
2459 assert_eq!(request.params(), &serde_json::to_value(&initialize)?);
2460 assert_eq!(pending_initialize(&compat), Some(ProtocolVersionKind::V2));
2461
2462 let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
2464 let error = compat
2465 .incoming_message(UntypedMessage::new(
2466 "proxy/initialize",
2467 serde_json::json!({ "protocolVersion": 1, "clientCapabilities": {} }),
2468 )?)
2469 .expect_err("a v2 proxy supports no version at or below v1");
2470 assert_eq!(i32::from(error.code), -32022);
2471 assert_eq!(
2472 error.data,
2473 Some(serde_json::json!({ "requested": 1, "supported": [2] }))
2474 );
2475 assert_eq!(pending_initialize(&compat), None);
2476 Ok(())
2477 }
2478
2479 #[test]
2480 fn exact_version_proxy_initialize_is_unchanged() -> Result<(), crate::Error> {
2481 let compat = ProtocolCompat::new(ProtocolMode::v1_proxy());
2482 let params = serde_json::json!({
2483 "protocolVersion": 1,
2484 "clientCapabilities": {},
2485 "_futureField": true
2486 });
2487 let request = compat
2488 .incoming_message(UntypedMessage::new("proxy/initialize", params.clone())?)?;
2489 assert_eq!(request.params(), ¶ms);
2490
2491 let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
2492 let params = serde_json::to_value(v2_initialize_request(ProtocolVersion::V2))?;
2493 let request = compat
2494 .incoming_message(UntypedMessage::new("proxy/initialize", params.clone())?)?;
2495 assert_eq!(request.params(), ¶ms);
2496 Ok(())
2497 }
2498
2499 #[test]
2500 fn proxy_initialize_requests_reject_wrong_directions_and_wrapping()
2501 -> Result<(), crate::Error> {
2502 for mode in [ProtocolMode::v1_proxy(), ProtocolMode::v2_proxy()] {
2503 let compat = ProtocolCompat::new(mode);
2504 for error in [
2505 compat
2506 .incoming_message(UntypedMessage::new(
2507 "initialize",
2508 serde_json::json!({ "protocolVersion": ProtocolVersion::V2 }),
2509 )?)
2510 .expect_err("proxy initialization must arrive through `proxy/initialize`"),
2511 compat
2512 .incoming_message(UntypedMessage::new(
2513 "proxy/successor",
2514 serde_json::json!({
2515 "method": "initialize",
2516 "params": { "protocolVersion": ProtocolVersion::V2 }
2517 }),
2518 )?)
2519 .expect_err("successor traffic must not carry initialization"),
2520 compat
2521 .incoming_message(UntypedMessage::new(
2522 "proxy/successor",
2523 serde_json::json!({
2524 "method": "proxy/successor",
2525 "params": {
2526 "method": "proxy/initialize",
2527 "params": { "protocolVersion": ProtocolVersion::V2 }
2528 }
2529 }),
2530 )?)
2531 .expect_err("nested successor traffic must not carry initialization"),
2532 compat
2533 .outgoing_message(
2534 UntypedMessage::new(
2535 "initialize",
2536 serde_json::json!({ "protocolVersion": ProtocolVersion::V2 }),
2537 )?,
2538 RemoteStyle::Predecessor,
2539 )
2540 .expect_err("downstream proxy initialization must use `proxy/successor`"),
2541 compat
2542 .outgoing_message(
2543 UntypedMessage::new(
2544 "proxy/initialize",
2545 serde_json::json!({ "protocolVersion": ProtocolVersion::V2 }),
2546 )?,
2547 RemoteStyle::Successor,
2548 )
2549 .expect_err("a proxy must not send `proxy/initialize` downstream"),
2550 ] {
2551 let data = error
2552 .data
2553 .as_ref()
2554 .and_then(|data| data.as_str())
2555 .unwrap_or_default();
2556 assert!(data.contains("proxy/initialize"), "{error:?}");
2557 assert!(data.contains("proxy/successor"), "{error:?}");
2558 }
2559 assert_eq!(pending_initialize(&compat), None);
2560 }
2561
2562 Ok(())
2563 }
2564
2565 #[test]
2566 fn outgoing_initialize_rejects_non_object_params() -> Result<(), crate::Error> {
2567 for (compat, message, remote_style) in [
2568 (
2569 ProtocolCompat::new(ProtocolMode::v2_client()),
2570 UntypedMessage::new("initialize", serde_json::Value::Null)?,
2571 RemoteStyle::Counterpart,
2572 ),
2573 (
2574 ProtocolCompat::new(ProtocolMode::v2_proxy()),
2575 UntypedMessage::new("initialize", serde_json::Value::Null)?,
2576 RemoteStyle::Successor,
2577 ),
2578 ] {
2579 let error = compat
2580 .outgoing_message(message, remote_style)
2581 .expect_err("initialize params must be an object");
2582 let data = error
2583 .data
2584 .as_ref()
2585 .and_then(|data| data.as_str())
2586 .unwrap_or_default();
2587 assert!(data.contains("protocolVersion"), "{error:?}");
2588 assert_eq!(pending_initialize(&compat), None);
2589 }
2590
2591 Ok(())
2592 }
2593
2594 #[test]
2595 fn outgoing_initialize_rejects_explicit_successor_wrapping() -> Result<(), crate::Error> {
2596 for message in [
2597 UntypedMessage::new(
2598 "proxy/successor",
2599 serde_json::json!({
2600 "method": "initialize",
2601 "params": { "protocolVersion": ProtocolVersion::V1 }
2602 }),
2603 )?,
2604 UntypedMessage::new(
2605 "proxy/successor",
2606 serde_json::json!({
2607 "method": "proxy/successor",
2608 "params": {
2609 "method": "initialize",
2610 "params": { "protocolVersion": ProtocolVersion::V1 }
2611 }
2612 }),
2613 )?,
2614 ] {
2615 let compat = ProtocolCompat::new(ProtocolMode::v2_proxy());
2616 let error = compat
2617 .outgoing_message(message, RemoteStyle::Successor)
2618 .expect_err("connection routing must own successor wrapping");
2619 let data = error
2620 .data
2621 .as_ref()
2622 .and_then(|data| data.as_str())
2623 .unwrap_or_default();
2624 assert!(data.contains("logical `initialize`"), "{error:?}");
2625 assert!(data.contains("proxy/successor"), "{error:?}");
2626 assert_eq!(pending_initialize(&compat), None);
2627 }
2628
2629 Ok(())
2630 }
2631
2632 #[test]
2633 fn proxy_initialize_round_trip_uses_the_selected_protocol_version()
2634 -> Result<(), crate::Error> {
2635 for (mode, selected, selected_kind) in [
2636 (
2637 ProtocolMode::v1_proxy(),
2638 ProtocolVersion::V1,
2639 ProtocolVersionKind::V1,
2640 ),
2641 (
2642 ProtocolMode::v2_proxy(),
2643 ProtocolVersion::V2,
2644 ProtocolVersionKind::V2,
2645 ),
2646 ] {
2647 let compat = ProtocolCompat::new(mode);
2648 let request = compat.incoming_message(UntypedMessage::new(
2649 "proxy/initialize",
2650 serde_json::json!({ "protocolVersion": selected }),
2651 )?)?;
2652 assert_eq!(
2653 required_protocol_version_from_value(request.params())?,
2654 selected
2655 );
2656 assert_eq!(pending_initialize(&compat), Some(selected_kind));
2657
2658 let response = compat.outgoing_response(
2659 "proxy/initialize",
2660 Ok(serde_json::json!({ "protocolVersion": selected })),
2661 )?;
2662 assert_eq!(required_protocol_version_from_value(&response)?, selected);
2663 assert_eq!(negotiated(&compat), selected_kind);
2664 assert_eq!(pending_initialize(&compat), None);
2665 assert_eq!(compat.active_wire_version(), selected_kind);
2666 }
2667
2668 Ok(())
2669 }
2670
2671 #[test]
2672 fn proxy_initialize_response_rejects_the_wrong_version_and_clears_pending_state()
2673 -> Result<(), crate::Error> {
2674 for (mode, selected, unsupported, selected_kind) in [
2675 (
2676 ProtocolMode::v1_proxy(),
2677 ProtocolVersion::V1,
2678 ProtocolVersion::V2,
2679 ProtocolVersionKind::V1,
2680 ),
2681 (
2682 ProtocolMode::v2_proxy(),
2683 ProtocolVersion::V2,
2684 ProtocolVersion::V1,
2685 ProtocolVersionKind::V2,
2686 ),
2687 ] {
2688 let compat = ProtocolCompat::new(mode);
2689 let request = compat.outgoing_message(
2690 UntypedMessage::new(
2691 "initialize",
2692 serde_json::json!({ "protocolVersion": unsupported }),
2693 )?,
2694 RemoteStyle::Successor,
2695 )?;
2696 assert_eq!(
2697 required_protocol_version_from_value(request.params())?,
2698 selected
2699 );
2700 assert_eq!(pending_initialize(&compat), Some(selected_kind));
2701
2702 let error = compat
2703 .incoming_response(
2704 "initialize",
2705 false,
2706 Ok(serde_json::json!({ "protocolVersion": unsupported })),
2707 )
2708 .expect_err("proxy initialization must reject a mismatched response version");
2709 let data = error
2710 .data
2711 .as_ref()
2712 .and_then(|data| data.as_str())
2713 .unwrap_or_default();
2714 assert!(
2715 data.contains(&format!(
2716 "required ACP protocol version {selected} but peer negotiated {unsupported}"
2717 )),
2718 "{error:?}"
2719 );
2720 assert_eq!(negotiated(&compat), selected_kind);
2721 assert_eq!(pending_initialize(&compat), None);
2722 assert_eq!(compat.active_wire_version(), selected_kind);
2723 }
2724
2725 Ok(())
2726 }
2727
2728 #[test]
2729 #[should_panic(expected = "cannot merge ACP builders with different API protocol versions")]
2730 fn merging_different_api_protocol_modes_panics() {
2731 let _ = ProtocolMode::v1_agent().merge(ProtocolMode::v2_agent());
2732 }
2733
2734 #[test]
2735 #[should_panic(expected = "cannot merge standard ACP and proxy ACP builders")]
2736 fn merging_standard_and_proxy_protocol_modes_panics() {
2737 let _ = ProtocolMode::v1_agent().merge(ProtocolMode::v1_proxy());
2738 }
2739 }
2740}
2741
2742pub(crate) use imp::{ProtocolCompat, ProtocolMode};