Skip to main content

objectiveai_sdk/client_objectiveai_mcp/client_request/
payload.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4/// Tagged union of request shapes the client-app layer can push down
5/// the reverse-attach channel.
6///
7/// Currently a single client → API variant: `McpListChanged`. The CLI's
8/// upstream `mcp::Connection` fired
9/// `notifications/{tools,resources}/list_changed` and the API
10/// re-emits it as an SSE event on the matching per-MCP GET stream —
11/// `/objectiveai` or `/{owner}/{name}/{ver}/{mcp}`, routed by
12/// `X-OBJECTIVEAI-RESPONSE-ID`.
13///
14/// The wire envelope's `id` field always belongs to whichever side
15/// originated the request; the receiver's `client_response::Response`
16/// echoes the same `id`.
17#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
18#[schemars(rename = "client_objectiveai_mcp.client_request.Payload")]
19#[serde(tag = "type", rename_all = "snake_case")]
20pub enum Payload {
21    /// The CLI's upstream `mcp::Connection` for `mcp_kind` fired
22    /// `notifications/<kind>/list_changed`. The API dispatches this
23    /// onto its per-`(response_id, McpKind)` broadcast so the
24    /// matching MCP GET-SSE listener sees a standard MCP
25    /// notification frame.
26    #[schemars(title = "McpListChanged")]
27    McpListChanged(McpListChanged),
28
29    /// Run the proxy's aggregated `tools/list` for `response_id` — the
30    /// same operation the proxy's HTTP `tools/list` endpoint performs
31    /// (fan out to every upstream in that session). Session-scoped, so
32    /// no `mcp_kind`; carries the normal MCP `tools/list` params.
33    #[schemars(title = "ListTools")]
34    ListTools {
35        response_id: String,
36        /// Restrict the listing to the single server with this name (the
37        /// proxy's routing prefix). `None` fans out to every upstream.
38        #[serde(default, skip_serializing_if = "Option::is_none")]
39        #[schemars(extend("omitempty" = true))]
40        name: Option<String>,
41        #[serde(flatten)]
42        params: crate::mcp::tool::ListToolsRequest,
43    },
44
45    /// Run the proxy's aggregated `tools/call` for `response_id` (routes
46    /// by tool-name prefix to the owning upstream). Unlike the HTTP
47    /// `tools/call`, this path does NOT consult the queue delegate.
48    #[schemars(title = "CallTool")]
49    CallTool {
50        response_id: String,
51        #[serde(flatten)]
52        params: crate::mcp::tool::CallToolRequestParams,
53    },
54
55    /// Run the proxy's aggregated `resources/list` for `response_id`.
56    #[schemars(title = "ListResources")]
57    ListResources {
58        response_id: String,
59        /// Restrict the listing to the single server with this name (the
60        /// proxy's routing prefix). `None` fans out to every upstream.
61        #[serde(default, skip_serializing_if = "Option::is_none")]
62        #[schemars(extend("omitempty" = true))]
63        name: Option<String>,
64        #[serde(flatten)]
65        params: crate::mcp::resource::ListResourcesRequest,
66    },
67
68    /// List the proxy's connected upstream MCP servers for `response_id`,
69    /// with each server's initialize metadata. A proxy-local aggregate
70    /// answered from its in-memory connection set — no MCP params, no
71    /// upstream round-trip.
72    #[schemars(title = "ListServers")]
73    ListServers { response_id: String },
74
75    /// Run the proxy's `resources/read` for `response_id` (routes by URI
76    /// prefix to the owning upstream).
77    #[schemars(title = "ReadResource")]
78    ReadResource {
79        response_id: String,
80        #[serde(flatten)]
81        params: crate::mcp::resource::ReadResourceRequestParams,
82    },
83}
84
85/// Payload for [`Payload::McpListChanged`].
86#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
87#[schemars(rename = "client_objectiveai_mcp.client_request.McpListChanged")]
88pub struct McpListChanged {
89    /// Which CLI-hosted MCP server fired the list-changed
90    /// notification. The API uses this to look up the right
91    /// per-MCP SSE broadcast and republish a standard MCP
92    /// notification frame to that upstream's proxy subscriber.
93    pub mcp_kind: super::super::McpKind,
94    /// Which catalog changed.
95    pub kind: McpListChangedKind,
96}
97
98/// Distinguishes `tools/list_changed` from `resources/list_changed`.
99#[derive(
100    Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash,
101)]
102#[schemars(rename = "client_objectiveai_mcp.client_request.McpListChangedKind")]
103#[serde(rename_all = "snake_case")]
104pub enum McpListChangedKind {
105    Tools,
106    Resources,
107}
108
109impl McpListChangedKind {
110    /// JSON-RPC method name MCP uses on the wire for this notification
111    /// kind. Used by the API's GET-SSE handler when it emits the
112    /// translated frame to subscribers.
113    pub fn method(&self) -> &'static str {
114        match self {
115            McpListChangedKind::Tools => "notifications/tools/list_changed",
116            McpListChangedKind::Resources => {
117                "notifications/resources/list_changed"
118            }
119        }
120    }
121}