/mcp/call-tool/{channel}
Kind 2: One tool of the caller’s servers, called. The ask carries the params as JSON; the server sends exactly one message, a result or an error, and closes the connection.
The ask is kind 2 on /requests. Its
payload is the JSON of the params defined below, running to the end
of the message. The server answers on /mcp/call-tool/{channel} by
sending exactly one message and closing 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.
A path that the proxy or the server closes cleanly with no message
before the close states that the ask was not served. An abrupt end of the path, and a /requests connection
that ends before the server opens the path, leave the exchange
unanswered, and the proxy asks it again, as
MCP Exchanges provides.
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)),
}
}
}