/tool/call-tool
One tool of the MCP server of the container, called. 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/call-tool and sends exactly one message, the
params defined below as JSON — the params of the MCP method
tools/call. 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/call_tool/request/request.rs:
//! Asking for a tool to be run.
use rmcp::model::{CallToolRequestParams};
use serde_json::Error;
use crate::decode::Decode;
use crate::encode::{Encode, Writer};
/// The name of a tool and what to call it with.
///
/// What an argument means belongs to the tool, and nothing
/// between here and it looks. The name is matched by the server that
/// offered it.
///
/// # 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 CallToolRequestParams,
);
/// 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/call_tool/response/frame.rs:
//! What running a tool produced.
use rmcp::ErrorData;
use rmcp::model::CallToolResult;
use super::super::super::FrameError;
use crate::decode::Decode;
use crate::encode::{Encode, Writer};
/// What the tool produced, or the reason it produced nothing.
///
/// 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 tool call is answered once. There is no head, no body, and
/// nothing to reassemble.
///
/// # 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 {
/// What the tool returned, including whether it considers
/// itself to have failed. Tag `0`.
Result(CallToolResult),
/// 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)),
}
}
}