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}