feat: standardize resource-not-found error code (SEP-2164) (#899)
* feat: implement SEP-2164 resource not found errors * test: update protocol version utility expectations * feat: gate not-found code at server boundary --------- Co-authored-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
parent
8f5310b424
commit
95a8e961e0
3 changed files with 126 additions and 4 deletions
|
|
@ -22,7 +22,9 @@ impl<H: ServerHandler> Service<RoleServer> for H {
|
|||
request: <RoleServer as ServiceRole>::PeerReq,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> Result<<RoleServer as ServiceRole>::Resp, McpError> {
|
||||
match request {
|
||||
// `context` is moved into the dispatch below, so read the negotiated version first.
|
||||
let protocol_version = context.protocol_version();
|
||||
let result = match request {
|
||||
ClientRequest::InitializeRequest(request) => self
|
||||
.initialize(request.params, context)
|
||||
.await
|
||||
|
|
@ -127,7 +129,18 @@ impl<H: ServerHandler> Service<RoleServer> for H {
|
|||
.cancel_task(request.params, context)
|
||||
.await
|
||||
.map(ServerResult::CancelTaskResult),
|
||||
}
|
||||
};
|
||||
// SEP-2164: peers negotiating 2026-07-28+ get the standard INVALID_PARAMS code for
|
||||
// resource-not-found; older peers keep RESOURCE_NOT_FOUND. ISO `YYYY-MM-DD` versions
|
||||
// compare lexically the same as chronologically.
|
||||
let use_invalid_params =
|
||||
protocol_version.is_some_and(|v| v.as_str() >= ProtocolVersion::V_2026_07_28.as_str());
|
||||
result.map_err(|mut error| {
|
||||
if use_invalid_params && error.code == ErrorCode::RESOURCE_NOT_FOUND {
|
||||
error.code = ErrorCode::INVALID_PARAMS;
|
||||
}
|
||||
error
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_notification(
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ impl std::fmt::Display for ProtocolVersion {
|
|||
}
|
||||
|
||||
impl ProtocolVersion {
|
||||
pub const V_2026_07_28: Self = Self(Cow::Borrowed("2026-07-28"));
|
||||
pub const V_2025_11_25: Self = Self(Cow::Borrowed("2025-11-25"));
|
||||
pub const V_2025_06_18: Self = Self(Cow::Borrowed("2025-06-18"));
|
||||
pub const V_2025_03_26: Self = Self(Cow::Borrowed("2025-03-26"));
|
||||
|
|
@ -164,6 +165,7 @@ impl ProtocolVersion {
|
|||
Self::V_2025_03_26,
|
||||
Self::V_2025_06_18,
|
||||
Self::V_2025_11_25,
|
||||
Self::V_2026_07_28,
|
||||
];
|
||||
|
||||
/// Returns the string representation of this protocol version.
|
||||
|
|
@ -193,6 +195,7 @@ impl<'de> Deserialize<'de> for ProtocolVersion {
|
|||
"2025-03-26" => return Ok(ProtocolVersion::V_2025_03_26),
|
||||
"2025-06-18" => return Ok(ProtocolVersion::V_2025_06_18),
|
||||
"2025-11-25" => return Ok(ProtocolVersion::V_2025_11_25),
|
||||
"2026-07-28" => return Ok(ProtocolVersion::V_2026_07_28),
|
||||
_ => {}
|
||||
}
|
||||
Ok(ProtocolVersion(Cow::Owned(s)))
|
||||
|
|
@ -541,9 +544,12 @@ impl ErrorData {
|
|||
data,
|
||||
}
|
||||
}
|
||||
/// Resource-not-found error (`-32002`). The server upgrades this to `INVALID_PARAMS`
|
||||
/// (`-32602`) for peers negotiating protocol `2026-07-28` or newer (SEP-2164).
|
||||
pub fn resource_not_found(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
|
||||
Self::new(ErrorCode::RESOURCE_NOT_FOUND, message, data)
|
||||
}
|
||||
|
||||
pub fn parse_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
|
||||
Self::new(ErrorCode::PARSE_ERROR, message, data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -672,6 +672,16 @@ impl<R: ServiceRole> RequestContext<R> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
impl RequestContext<RoleServer> {
|
||||
/// The protocol version the client negotiated, or `None` before peer info is recorded.
|
||||
pub fn protocol_version(&self) -> Option<crate::model::ProtocolVersion> {
|
||||
self.peer
|
||||
.peer_info()
|
||||
.map(|info| info.protocol_version.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Request execution context
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
|
|
|
|||
Loading…
Reference in a new issue