Skip to main content

objectiveai_sdk/cli/command/
command_response.rs

1/// Convert a typed CLI response item into its MCP-shaped projection —
2/// either a media [`ContentBlock`] for MCP tool results or a JSONL
3/// `serde_json::Value` for the line-oriented stream wire format.
4/// Implementors are the per-leaf response shapes in the surrounding
5/// tree (`agents::spawn::ResponseItem`, `tools::run::ResponseItem`, …)
6/// — not yet wired up; the trait exists to anchor the contract.
7#[cfg(feature = "mcp")]
8pub trait CommandResponse {
9    fn into_mcp(self) -> McpResponseItem;
10}
11
12/// MCP-side projection of one CLI response item.
13///
14/// - [`McpResponseItem::Media`] carries a typed [`ContentBlock`] for
15///   image / audio / file payloads that ride the MCP tool result as
16///   real media blocks.
17/// - [`McpResponseItem::JSONL`] carries a `serde_json::Value` for the
18///   line-oriented JSONL wire format every other leaf emits.
19///
20/// Not serde-shaped on purpose: this is the runtime carrier between
21/// `CommandResponse::into_mcp` and the formatter; nothing on either
22/// side needs to round-trip it through JSON.
23#[cfg(feature = "mcp")]
24#[derive(Debug, Clone)]
25pub enum McpResponseItem {
26    Media(crate::mcp::tool::ContentBlock),
27    JSONL(serde_json::Value),
28}
29
30// Bare-media leaf impls. Every log media leaf under
31// `cli/command/logs/agents/completions/{request,response}/messages/.../{get,subscribe}`
32// declares `pub type Response = <media type>` for one of the four media
33// types below, so these four impls cover all 20 such leaves through the
34// type aliases. Each `self.into()` reaches the matching
35// `From<T> for ContentBlock` impl in `mcp/tool/content_block.rs`.
36
37#[cfg(feature = "mcp")]
38impl CommandResponse for crate::agent::completions::message::ImageUrl {
39    fn into_mcp(self) -> McpResponseItem {
40        McpResponseItem::Media(self.into())
41    }
42}
43
44#[cfg(feature = "mcp")]
45impl CommandResponse for crate::agent::completions::message::InputAudio {
46    fn into_mcp(self) -> McpResponseItem {
47        McpResponseItem::Media(self.into())
48    }
49}
50
51#[cfg(feature = "mcp")]
52impl CommandResponse for crate::agent::completions::message::VideoUrl {
53    fn into_mcp(self) -> McpResponseItem {
54        McpResponseItem::Media(self.into())
55    }
56}
57
58#[cfg(feature = "mcp")]
59impl CommandResponse for crate::agent::completions::message::File {
60    fn into_mcp(self) -> McpResponseItem {
61        McpResponseItem::Media(self.into())
62    }
63}
64
65// JSONL passthrough — `serde_json::to_value(self)` is the body for
66// every alias target whose leaf simply emits its serde-shaped value
67// as one JSONL line. Special-cased shortcuts: `String` constructs
68// `Value::String` directly; `serde_json::Value` rides through as-is;
69// `Option<T>` delegates to `T::into_mcp` on `Some` and emits a JSONL
70// null on `None`.
71
72#[cfg(feature = "mcp")]
73impl CommandResponse for String {
74    fn into_mcp(self) -> McpResponseItem {
75        McpResponseItem::JSONL(serde_json::Value::String(self))
76    }
77}
78
79#[cfg(feature = "mcp")]
80impl CommandResponse for serde_json::Value {
81    fn into_mcp(self) -> McpResponseItem {
82        McpResponseItem::JSONL(self)
83    }
84}
85
86#[cfg(feature = "mcp")]
87impl CommandResponse for schemars::Schema {
88    fn into_mcp(self) -> McpResponseItem {
89        McpResponseItem::JSONL(serde_json::to_value(self).unwrap())
90    }
91}
92
93#[cfg(feature = "mcp")]
94impl CommandResponse for crate::cli::command::ResponseSchema {
95    fn into_mcp(self) -> McpResponseItem {
96        self.0.into_mcp()
97    }
98}
99
100#[cfg(feature = "mcp")]
101impl<T: CommandResponse> CommandResponse for Option<T> {
102    fn into_mcp(self) -> McpResponseItem {
103        match self {
104            Some(v) => v.into_mcp(),
105            None => McpResponseItem::JSONL(serde_json::Value::Null),
106        }
107    }
108}
109
110#[cfg(feature = "mcp")]
111impl<T: CommandResponse> CommandResponse for Result<T, crate::cli::Error> {
112    fn into_mcp(self) -> McpResponseItem {
113        match self {
114            Ok(v) => v.into_mcp(),
115            Err(e) => e.into_mcp(),
116        }
117    }
118}
119
120#[cfg(feature = "mcp")]
121impl CommandResponse for crate::agent::completions::message::AssistantToolCallDelta {
122    fn into_mcp(self) -> McpResponseItem {
123        McpResponseItem::JSONL(
124            serde_json::to_value(self).unwrap(),
125        )
126    }
127}
128
129#[cfg(feature = "mcp")]
130impl CommandResponse for crate::agent::completions::message::MessageLog {
131    fn into_mcp(self) -> McpResponseItem {
132        McpResponseItem::JSONL(
133            serde_json::to_value(self).unwrap(),
134        )
135    }
136}
137
138#[cfg(feature = "mcp")]
139impl CommandResponse for crate::agent::completions::request::AgentCompletionCreateParamsLog {
140    fn into_mcp(self) -> McpResponseItem {
141        McpResponseItem::JSONL(
142            serde_json::to_value(self).unwrap(),
143        )
144    }
145}
146
147#[cfg(feature = "mcp")]
148impl CommandResponse for crate::agent::completions::response::Logprobs {
149    fn into_mcp(self) -> McpResponseItem {
150        McpResponseItem::JSONL(
151            serde_json::to_value(self).unwrap(),
152        )
153    }
154}
155
156#[cfg(feature = "mcp")]
157impl CommandResponse for crate::agent::completions::response::streaming::AgentCompletionChunkLog {
158    fn into_mcp(self) -> McpResponseItem {
159        McpResponseItem::JSONL(
160            serde_json::to_value(self).unwrap(),
161        )
162    }
163}
164
165#[cfg(feature = "mcp")]
166impl CommandResponse for crate::agent::response::GetAgentResponse {
167    fn into_mcp(self) -> McpResponseItem {
168        McpResponseItem::JSONL(
169            serde_json::to_value(self).unwrap(),
170        )
171    }
172}
173
174#[cfg(feature = "mcp")]
175impl CommandResponse for crate::functions::executions::request::FunctionExecutionCreateParamsLog {
176    fn into_mcp(self) -> McpResponseItem {
177        McpResponseItem::JSONL(
178            serde_json::to_value(self).unwrap(),
179        )
180    }
181}
182
183#[cfg(feature = "mcp")]
184impl CommandResponse
185    for crate::functions::executions::response::streaming::FunctionExecutionChunkLog
186{
187    fn into_mcp(self) -> McpResponseItem {
188        McpResponseItem::JSONL(
189            serde_json::to_value(self).unwrap(),
190        )
191    }
192}
193
194#[cfg(feature = "mcp")]
195impl CommandResponse for crate::functions::inventions::request::FunctionInventionCreateParams {
196    fn into_mcp(self) -> McpResponseItem {
197        McpResponseItem::JSONL(
198            serde_json::to_value(self).unwrap(),
199        )
200    }
201}
202
203#[cfg(feature = "mcp")]
204impl CommandResponse
205    for crate::functions::inventions::response::streaming::FunctionInventionChunkLog
206{
207    fn into_mcp(self) -> McpResponseItem {
208        McpResponseItem::JSONL(
209            serde_json::to_value(self).unwrap(),
210        )
211    }
212}
213
214#[cfg(feature = "mcp")]
215impl CommandResponse
216    for crate::functions::inventions::recursive::request::FunctionInventionRecursiveCreateParamsLog
217{
218    fn into_mcp(self) -> McpResponseItem {
219        McpResponseItem::JSONL(
220            serde_json::to_value(self).unwrap(),
221        )
222    }
223}
224
225#[cfg(feature = "mcp")]
226impl CommandResponse
227    for crate::functions::inventions::recursive::response::streaming::FunctionInventionRecursiveChunkLog
228{
229    fn into_mcp(self) -> McpResponseItem {
230        McpResponseItem::JSONL(
231            serde_json::to_value(self).unwrap(),
232        )
233    }
234}
235
236#[cfg(feature = "mcp")]
237impl CommandResponse
238    for crate::functions::inventions::state::response::GetFunctionInventionStateResponse
239{
240    fn into_mcp(self) -> McpResponseItem {
241        McpResponseItem::JSONL(
242            serde_json::to_value(self).unwrap(),
243        )
244    }
245}
246
247#[cfg(feature = "mcp")]
248impl CommandResponse for crate::functions::profiles::response::GetProfileResponse {
249    fn into_mcp(self) -> McpResponseItem {
250        McpResponseItem::JSONL(
251            serde_json::to_value(self).unwrap(),
252        )
253    }
254}
255
256#[cfg(feature = "mcp")]
257impl CommandResponse for crate::functions::response::GetFunctionResponse {
258    fn into_mcp(self) -> McpResponseItem {
259        McpResponseItem::JSONL(
260            serde_json::to_value(self).unwrap(),
261        )
262    }
263}
264
265#[cfg(feature = "mcp")]
266impl CommandResponse for crate::Remote {
267    fn into_mcp(self) -> McpResponseItem {
268        McpResponseItem::JSONL(
269            serde_json::to_value(self).unwrap(),
270        )
271    }
272}
273
274#[cfg(feature = "mcp")]
275impl CommandResponse for crate::swarm::response::GetSwarmResponse {
276    fn into_mcp(self) -> McpResponseItem {
277        McpResponseItem::JSONL(
278            serde_json::to_value(self).unwrap(),
279        )
280    }
281}
282
283#[cfg(feature = "mcp")]
284impl CommandResponse for crate::vector::completions::request::VectorCompletionCreateParams {
285    fn into_mcp(self) -> McpResponseItem {
286        McpResponseItem::JSONL(
287            serde_json::to_value(self).unwrap(),
288        )
289    }
290}
291
292#[cfg(feature = "mcp")]
293impl CommandResponse
294    for crate::vector::completions::response::streaming::VectorCompletionChunkLog
295{
296    fn into_mcp(self) -> McpResponseItem {
297        McpResponseItem::JSONL(
298            serde_json::to_value(self).unwrap(),
299        )
300    }
301}