/tool/list-resources

The resources the MCP server of the container offers, one page at a time. The server sends the params as JSON; the proxy sends exactly one message, a result or an error, and closes the connection.

The server opens /tool/list-resources and sends exactly one message, the params defined below as JSON — the params of the MCP method resources/list. The proxy sends exactly one message and closes the connection. The message is the response frame defined below: its first byte is 0, followed by the result as JSON, or 1, followed by the error as JSON. Tool states what the proxy sends when the params do not parse and when the MCP server of the container cannot be reached.

The params are defined by diverge-provider-sdk/src/shared/mcp/list_resources/request/request.rs:

//! Asking what resources there are.

use rmcp::model::{PaginatedRequestParams};
use serde_json::Error;

use crate::decode::Decode;
use crate::encode::{Encode, Writer};

/// Where in the listing to start, if anywhere in particular.
///
/// The same shape [`list_tools`](crate::shared::mcp::list_tools) has, for
/// the same reason: it is the same MCP request against a different
/// noun.
///
/// # It is a wrapper, and a thin one
///
/// [`rmcp`] already defines the shape and this crate has no business
/// redefining it — an MCP request has one form and it is MCP's. What
/// the wrapper adds is the two impls beneath it, so that a channel
/// request carrying one writes `request.encode(out)` like every other
/// payload in this crate rather than reaching for `serde_json` itself.
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Request(
    /// The parameters, as [`rmcp`] defines them.
    pub Option<PaginatedRequestParams>,
);

/// Its JSON, and nothing in front of it. The tag that says which
/// request this is belongs to whichever frame carries it.
impl Encode for Request {
    /// The ordinary JSON failure.
    type Error = Error;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Error> {
        serde_json::to_writer(out, &self.0)
    }
}

impl Decode<'_> for Request {
    /// The ordinary JSON failure. There is nothing else here to get
    /// wrong — no tag to be unknown, and no empty case, since no bytes
    /// at all is a JSON document that ended too early and is reported
    /// as one.
    type Error = Error;

    fn decode(bytes: &[u8]) -> Result<Self, Error> {
        serde_json::from_slice(bytes).map(Request)
    }
}

The answer is defined by diverge-provider-sdk/src/shared/mcp/list_resources/response/frame.rs:

//! What resources a server offers.

use rmcp::ErrorData;
use rmcp::model::ListResourcesResult;

use super::super::super::FrameError;
use crate::decode::Decode;
use crate::encode::{Encode, Writer};

/// The resources, or the reason there are none to report.
///
/// A payload leads with one byte saying which — `0` for
/// [`Result`](Self::Result), `1` for [`Error`](Self::Error) — and the
/// rest is that variant's own JSON.
///
/// # One frame, then the finish
///
/// A listing is answered once. A server with more to say says so
/// with a cursor, and the next page is another exchange.
///
/// # The error is [`rmcp`]'s, not this crate's
///
/// Everywhere else in this protocol a failure travels as
/// [`shared::error::Error`](crate::shared::error::Error), which is one
/// opaque JSON value, because everywhere else the failure is the
/// PROVIDER's own and it is nobody's business what it says.
///
/// This one is not the provider's. It is an MCP server's, relayed, and
/// its JSON-RPC code is content rather than detail: `-32601` is "no
/// such tool" and `-32602` is "the arguments were wrong", and an agent
/// told only that something failed cannot tell those apart or act
/// differently on them.
///
/// So the code goes through, along with the message and whatever data
/// came with it. A relay that flattened them would be deciding that an
/// MCP error means less than MCP says it does.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
    /// The resources, and a cursor if there are more. Tag `0`.
    Result(ListResourcesResult),
    /// The server refused or could not answer. Tag `1`.
    ///
    /// See the type's own documentation for why this is
    /// [`ErrorData`] rather than the error every other endpoint uses.
    Error(ErrorData),
}

/// Tag for [`Frame::Result`].
const RESULT: u8 = 0;

/// Tag for [`Frame::Error`].
const ERROR: u8 = 1;

impl Encode for Frame {
    /// The ordinary JSON failure. Both variants are serialized and the
    /// tag cannot fail.
    type Error = serde_json::Error;

    // Spelled out rather than `Self::Error`: this enum has a variant
    // called `Error`, so the associated type is ambiguous by that name.
    fn encode(&self, out: &mut Writer<'_>) -> Result<(), serde_json::Error> {
        match self {
            Frame::Result(value) => {
                out.extend_from_slice(&[RESULT]);
                serde_json::to_writer(out, value)
            }
            Frame::Error(error) => {
                out.extend_from_slice(&[ERROR]);
                serde_json::to_writer(out, error)
            }
        }
    }
}

impl Decode<'_> for Frame {
    /// Three ways to fail, and only one of them is JSON.
    type Error = FrameError;

    // Spelled out for the same reason as `encode` above.
    fn decode(bytes: &[u8]) -> Result<Self, FrameError> {
        let (tag, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
        match *tag {
            RESULT => serde_json::from_slice(rest)
                .map(Frame::Result)
                .map_err(FrameError::Body),
            ERROR => serde_json::from_slice(rest)
                .map(Frame::Error)
                .map_err(FrameError::Body),
            tag => Err(FrameError::UnknownTag(tag)),
        }
    }
}