# Request

> The payload of the channel request: the tag byte 0 and the request of the channel.

Canonical: https://protocol.diverge.network/2.3.0/container-proxy-endpoints/tools-begin/client/postgres/request/
Specification revision: 2.3.0

```text
[0][connection_id: u32, big-endian]
```

- **`connection_id`.** The id the server announced on its own postgres
  channel, as four bytes, big-endian.
- **Malformed.** A channel request whose payload does not decode as
  stated is answered by a channel response finish that no channel
  response precedes, and the server does nothing else for it.

The channel request is defined by `diverge-sdk-rs/src/container_proxy/outside/endpoints/tools/begin/client/channel_request/frame.rs`:

```rust
//! What a client's channel request frame carries for a tool container
//! begin.

use std::error::Error;
use std::fmt;

use crate::wire::decode::Decode;
use crate::wire::encode::{Encode, Writer};
use crate::shared::containers::postgres;
use crate::shared::containers::daemon;
use crate::shared::containers::secrets::Values;
use crate::shared::mcp;

/// What the server asks the proxy for once a tool container has
/// begun.
///
/// A payload leads with one byte saying which, and the rest is that
/// variant's own bytes.
///
/// | tag | asks for |
/// |-----|----------|
/// | `0` | [`Postgres`](Self::Postgres) |
/// | `1` | [`Info`](Self::Info) |
/// | `2` | [`Daemon`](Self::Daemon) |
/// | `3` | [`McpListTools`](Self::McpListTools) |
/// | `4` | [`McpListResources`](Self::McpListResources) |
/// | `5` | [`McpCallTool`](Self::McpCallTool) |
/// | `6` | [`McpReadResource`](Self::McpReadResource) |
/// | `7` | [`McpNotifications`](Self::McpNotifications) |
/// | `8` | [`Secrets`](Self::Secrets) |
///
/// The first two are the same in both begin scopes, so a reader of
/// one is a reader of both; what follows is this family's own
/// exchange, and then the feed, which both scopes have again under a
/// tag of their own. All of them reach INTO the container: the last
/// hop of what a caller opened on the provider, or of what the caller
/// fed.
#[derive(Debug, Clone, PartialEq)]
pub enum Frame {
    /// The server's half of a database connection. Tag `0`.
    ///
    /// Opened once the server has taken the proxy's half, quoting the
    /// same connection; what comes back is everything the container's
    /// driver wrote. See
    /// [`postgres`](crate::shared::containers::postgres) for the pair.
    Postgres(postgres::request::Postgres),
    /// What the arguments may be. Tag `1`.
    ///
    /// Carries nothing — the variant is bare — and the proxy answers
    /// with the image's DIVERGE.md, parsed — read from the
    /// container's root, the program never asked — or that it has
    /// none. See [`info`](crate::shared::containers::info).
    Info,
    /// A daemon connection the program opened, by the id the proxy
    /// announced: the server's half of it. Tag `2`.
    ///
    /// What comes back on it is everything the PROGRAM sends, as
    /// [`daemon::client::Frame`](crate::shared::containers::daemon::client::Frame)s;
    /// its finish is the program's socket ended. See
    /// [`daemon`](crate::shared::containers::daemon) for the pair.
    Daemon(daemon::request::Daemon),
    /// What tools the container's server has. Tag `3`.
    ///
    /// The family's own exchange, and the first of five: the caller
    /// speaking to the MCP server inside, each of the exchanges in
    /// [`shared::mcp`](crate::shared::mcp) on a channel of its own,
    /// answered by the container's own server.
    McpListTools(mcp::list_tools::request::Request),
    /// What resources it has. Tag `4`.
    McpListResources(mcp::list_resources::request::Request),
    /// Run one of its tools. Tag `5`.
    McpCallTool(mcp::call_tool::request::Request),
    /// Read one of its resources. Tag `6`.
    McpReadResource(mcp::read_resource::request::Request),
    /// Everything it says on its own account. Tag `7`.
    ///
    /// Carries nothing, and answers for as long as the channel lives.
    McpNotifications(mcp::notifications::request::Request),
    /// The secrets' values, fed to the program. Tag `8`.
    ///
    /// Carries the [`Values`] as JSON — every name the program
    /// declared, each to its value — which the proxy hands the
    /// program on `POST /secrets` and holds until that call answers.
    /// Opened by the server once before it sends the run's id and
    /// again at every activation, relaying the caller's own feed.
    /// Answered once — by a
    /// [`feed::response::Frame`](crate::shared::containers::secrets::feed::response::Frame),
    /// `Fed` or the program's refusal — and then the finish; a `Fed`
    /// opens the gate the proxy holds every exchange behind. See
    /// [`secrets`](crate::shared::containers::secrets).
    Secrets(Values),
}

/// Tag for [`Frame::Postgres`].
const POSTGRES: u8 = 0;

/// Tag for [`Frame::Info`].
const INFO: u8 = 1;

/// Tag for [`Frame::Daemon`].
const DAEMON: u8 = 2;

/// Tag for [`Frame::McpListTools`].
const MCP_LIST_TOOLS: u8 = 3;

/// Tag for [`Frame::McpListResources`].
const MCP_LIST_RESOURCES: u8 = 4;

/// Tag for [`Frame::McpCallTool`].
const MCP_CALL_TOOL: u8 = 5;

/// Tag for [`Frame::McpReadResource`].
const MCP_READ_RESOURCE: u8 = 6;

/// Tag for [`Frame::McpNotifications`].
const MCP_NOTIFICATIONS: u8 = 7;

/// Tag for [`Frame::Secrets`].
const SECRETS: u8 = 8;

impl Encode for Frame {
    /// The ordinary JSON failure, from whichever ask has one.
    type Error = serde_json::Error;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
        match self {
            Frame::Postgres(request) => {
                out.extend_from_slice(&[POSTGRES]);
                // Its error is `Infallible`, and an empty match on one
                // is how you say so: there is no value to handle.
                request.encode(out).map_err(|error| match error {})
            }
            Frame::Info => {
                out.extend_from_slice(&[INFO]);
                Ok(())
            }
            Frame::Daemon(request) => {
                out.extend_from_slice(&[DAEMON]);
                request.encode(out).map_err(|error| match error {})
            }
            Frame::McpListTools(request) => {
                out.extend_from_slice(&[MCP_LIST_TOOLS]);
                request.encode(out)
            }
            Frame::McpListResources(request) => {
                out.extend_from_slice(&[MCP_LIST_RESOURCES]);
                request.encode(out)
            }
            Frame::McpCallTool(request) => {
                out.extend_from_slice(&[MCP_CALL_TOOL]);
                request.encode(out)
            }
            Frame::McpReadResource(request) => {
                out.extend_from_slice(&[MCP_READ_RESOURCE]);
                request.encode(out)
            }
            Frame::McpNotifications(request) => {
                out.extend_from_slice(&[MCP_NOTIFICATIONS]);
                request.encode(out).map_err(|error| match error {})
            }
            Frame::Secrets(values) => {
                out.extend_from_slice(&[SECRETS]);
                serde_json::to_writer(out, values)
            }
        }
    }
}

impl Decode<'_> for Frame {
    /// Several ways to fail, and the parses among them name which.
    type Error = FrameError;

    fn decode(bytes: &[u8]) -> Result<Self, Self::Error> {
        let (tag, rest) = bytes.split_first().ok_or(FrameError::Empty)?;
        match *tag {
            POSTGRES => postgres::request::Postgres::decode(rest)
                .map(Frame::Postgres)
                .map_err(FrameError::Postgres),
            INFO => Ok(Frame::Info),
            DAEMON => daemon::request::Daemon::decode(rest)
                .map(Frame::Daemon)
                .map_err(FrameError::Daemon),
            MCP_LIST_TOOLS => mcp::list_tools::request::Request::decode(rest)
                .map(Frame::McpListTools)
                .map_err(FrameError::McpParams),
            MCP_LIST_RESOURCES => {
                mcp::list_resources::request::Request::decode(rest)
                    .map(Frame::McpListResources)
                    .map_err(FrameError::McpParams)
            }
            MCP_CALL_TOOL => mcp::call_tool::request::Request::decode(rest)
                .map(Frame::McpCallTool)
                .map_err(FrameError::McpParams),
            MCP_READ_RESOURCE => {
                mcp::read_resource::request::Request::decode(rest)
                    .map(Frame::McpReadResource)
                    .map_err(FrameError::McpParams)
            }
            MCP_NOTIFICATIONS => Ok(Frame::McpNotifications(
                mcp::notifications::request::Request::decode(rest)
                    .unwrap_or_else(|error| match error {}),
            )),
            SECRETS => serde_json::from_slice(rest)
                .map(Frame::Secrets)
                .map_err(FrameError::Secrets),
            tag => Err(FrameError::UnknownTag(tag)),
        }
    }
}

/// A tools begin channel request that could not be read.
#[derive(Debug)]
pub enum FrameError {
    /// No bytes at all, so not even a tag.
    Empty,
    /// A tag that is none of this frame's.
    UnknownTag(u8),
    /// The connection id was not four bytes.
    Postgres(postgres::request::PostgresError),
    /// The daemon connection's id was not four bytes.
    Daemon(daemon::request::DaemonError),
    /// One of the five MCP exchanges' params did not parse.
    ///
    /// One variant for five tags, because they fail the same way and
    /// the tag already said which was meant.
    McpParams(serde_json::Error),
    /// The secrets' values did not parse as JSON. The message is the
    /// parser's, which quotes no value.
    Secrets(serde_json::Error),
}

impl fmt::Display for FrameError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FrameError::Empty => {
                f.write_str("tools begin channel request frame is empty")
            }
            FrameError::UnknownTag(tag) => {
                write!(f, "unknown tools begin channel request tag {tag}")
            }
            FrameError::Postgres(error) => write!(f, "{error}"),
            FrameError::Daemon(error) => write!(f, "{error}"),
            FrameError::McpParams(error) => {
                write!(f, "mcp request params did not parse: {error}")
            }
            FrameError::Secrets(error) => {
                write!(f, "secrets feed did not parse: {error}")
            }
        }
    }
}

impl Error for FrameError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            FrameError::McpParams(error) | FrameError::Secrets(error) => Some(error),
            FrameError::Postgres(error) => Some(error),
            FrameError::Daemon(error) => Some(error),
            FrameError::Empty | FrameError::UnknownTag(_) => None,
        }
    }
}
```

and by `diverge-sdk-rs/src/shared/containers/postgres/request/postgres.rs`:

```rust
//! Naming a connection.

use std::error::Error;
use std::fmt;

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

/// Which connection a channel is one half of.
///
/// The provider opens its half carrying this with an id it minted,
/// and the caller opens the other half quoting the same id. That is
/// the whole of the correlation: a caller with several connections in
/// flight learns which one it is being asked to feed.
///
/// # It is the provider's to choose and the provider's to keep unique
///
/// Unique among the connections this provider has open in this scope
/// — reusing one that is still live makes two connections
/// indistinguishable, and the provider is the only party that could
/// have prevented it. A number that counts up is the obvious way and
/// nothing requires it. Reusing one after both channels have finished
/// is fine; nothing here remembers.
///
/// # Why not the channel it arrived on
///
/// Because channels are numbered per SENDER. The provider opens its
/// half on a channel of its own, the caller opens the other on a
/// channel of its own, and neither side's header can name the other's
/// — so a payload quoting a channel number would be quoting one out of
/// a namespace its reader does not share. An id invented for the
/// connection belongs to neither channel, which is what makes it
/// readable on both sides — the same argument a
/// [`write_id`](crate::shared::containers::write_path::request::Request::write_id)
/// makes.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Postgres {
    /// What this connection is called, chosen by the provider.
    pub connection_id: u32,
}

/// The bytes a connection id occupies.
const CONNECTION_ID_LEN: usize = 4;

/// Four big-endian bytes. No serialization, because a fixed-width
/// integer does not need one.
impl Encode for Postgres {
    /// [`Infallible`](std::convert::Infallible): four known bytes.
    type Error = std::convert::Infallible;

    fn encode(&self, out: &mut Writer<'_>) -> Result<(), Self::Error> {
        out.extend_from_slice(&self.connection_id.to_be_bytes());
        Ok(())
    }
}

impl Decode<'_> for Postgres {
    /// One way to fail: the wrong number of bytes.
    type Error = PostgresError;

    fn decode(bytes: &[u8]) -> Result<Self, Self::Error> {
        <[u8; CONNECTION_ID_LEN]>::try_from(bytes)
            .map(|bytes| Postgres {
                connection_id: u32::from_be_bytes(bytes),
            })
            .map_err(|_| PostgresError::Length(bytes.len()))
    }
}

/// A Postgres channel request that could not be read.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PostgresError {
    /// A payload that was not four bytes, carrying however many there
    /// were.
    Length(usize),
}

impl fmt::Display for PostgresError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            PostgresError::Length(len) => {
                write!(f, "postgres connection request is {len} bytes, not 4")
            }
        }
    }
}

impl Error for PostgresError {}
```
