Skip to main content

objectiveai_sdk/cli/command/plugins/get/
mod.rs

1//! `plugins get` — async handler stub.
2
3use crate::cli::command::CommandRequest;
4
5#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
6#[schemars(rename = "cli.command.plugins.get.Request")]
7pub struct Request {
8    pub path_type: Path,
9    pub owner: String,
10    pub name: String,
11    pub version: String,
12    #[serde(flatten)]
13    pub base: crate::cli::command::RequestBase,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
17#[schemars(rename = "cli.command.plugins.get.Path")]
18pub enum Path {
19    #[serde(rename = "plugins/get")]
20    PluginsGet,
21}
22
23impl CommandRequest for Request {
24    fn into_command(&self) -> Vec<String> {
25        let mut argv = vec![
26            "plugins".to_string(),
27            "get".to_string(),
28            "--owner".to_string(),
29            self.owner.clone(),
30            "--name".to_string(),
31            self.name.clone(),
32            "--version".to_string(),
33            self.version.clone(),
34        ];
35        self.base.push_flags(&mut argv);
36        argv
37    }
38
39    fn request_base(&self) -> &crate::cli::command::RequestBase {
40        &self.base
41    }
42
43    fn request_base_mut(&mut self) -> Option<&mut crate::cli::command::RequestBase> {
44        Some(&mut self.base)
45    }
46}
47
48pub type Response = Option<ResponseManifest>;
49
50#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
51#[schemars(rename = "cli.command.plugins.get.ResponseManifest")]
52pub struct ResponseManifest {
53    pub owner: String,
54    pub name: String,
55    pub version: String,
56    pub description: String,
57    /// Per-OS exec argv for the plugin's cli side, run with CWD =
58    /// `<plugin dir>/cli/` — the same shape tools use. Required (an
59    /// empty exec is a viewer-only plugin, which still round-trips as
60    /// an empty per-OS object).
61    pub exec: crate::cli::command::tools::get::Exec,
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    #[schemars(extend("omitempty" = true))]
64    pub viewer_url: Option<String>,
65    #[serde(default, skip_serializing_if = "Vec::is_empty")]
66    pub viewer_routes: Vec<ResponseViewerRoute>,
67    #[serde(default, skip_serializing_if = "Vec::is_empty")]
68    pub mcp_servers: Vec<ResponseMcpServer>,
69}
70
71impl ResponseManifest {
72    /// LLM-visible MCP tool name for this plugin:
73    /// `plugin_{owner}_{name}_{version}`, with every `.` in the
74    /// version substituted to `-` so the result stays within the
75    /// Anthropic tool-name regex (`^[a-zA-Z0-9_-]{1,128}$`).
76    /// `objectiveai-mcp` advertises each plugin under this name.
77    pub fn tool_name(&self) -> String {
78        format!(
79            "plugin_{}_{}_{}",
80            self.owner,
81            self.name,
82            self.version.replace('.', "-")
83        )
84    }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
88#[schemars(rename = "cli.command.plugins.get.ResponseViewerRoute")]
89pub struct ResponseViewerRoute {
90    pub path: String,
91    pub method: ResponseHttpMethod,
92    pub r#type: String,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
96#[serde(rename_all = "UPPERCASE")]
97#[schemars(rename = "cli.command.plugins.get.ResponseHttpMethod")]
98pub enum ResponseHttpMethod {
99    Get,
100    Post,
101    Put,
102    Patch,
103    Delete,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
107#[schemars(rename = "cli.command.plugins.get.ResponseMcpServer")]
108pub struct ResponseMcpServer {
109    pub name: String,
110    pub authorization: bool,
111}
112
113#[derive(clap::Args)]
114pub struct Args {
115    /// Plugin owner (GitHub `<owner>` segment). Required.
116    #[arg(long)]
117    pub owner: String,
118    /// Plugin name (repository segment). Required.
119    #[arg(long)]
120    pub name: String,
121    /// Plugin version. Required.
122    #[arg(long)]
123    pub version: String,
124    #[command(flatten)]
125    pub base: crate::cli::command::RequestBaseArgs,
126}
127
128#[derive(clap::Args)]
129#[command(args_conflicts_with_subcommands = true)]
130pub struct Command {
131    #[command(flatten)]
132    pub args: Args,
133    #[command(subcommand)]
134    pub schema: Option<Schema>,
135}
136
137#[derive(clap::Subcommand)]
138pub enum Schema {
139    /// Emit the JSON Schema for this leaf's `Request` type and exit.
140    RequestSchema(request_schema::Args),
141    /// Emit the JSON Schema for this leaf's `Response` type and exit.
142    ResponseSchema(response_schema::Args),
143}
144
145impl TryFrom<Args> for Request {
146    type Error = crate::cli::command::FromArgsError;
147    fn try_from(args: Args) -> Result<Self, Self::Error> {
148        Ok(Self {
149            path_type: Path::PluginsGet,
150            owner: args.owner,
151            name: args.name,
152            version: args.version,
153            base: args.base.into(),
154        })
155    }
156}
157
158#[cfg(feature = "cli-executor")]
159pub async fn execute<E: crate::cli::command::CommandExecutor>(
160    executor: &E,
161    mut request: Request,
162
163        agent_arguments: Option<&crate::cli::command::AgentArguments>,
164    ) -> Result<Response, E::Error> {
165    request.base.clear_transform();
166    executor.execute_one(request, agent_arguments).await
167}
168
169#[cfg(feature = "cli-executor")]
170pub async fn execute_transform<E: crate::cli::command::CommandExecutor>(
171    executor: &E,
172    mut request: Request,
173    transform: crate::cli::command::Transform,
174
175        agent_arguments: Option<&crate::cli::command::AgentArguments>,
176    ) -> Result<serde_json::Value, E::Error> {
177    request.base.set_transform(transform);
178    executor.execute_one(request, agent_arguments).await
179}
180
181#[cfg(feature = "mcp")]
182impl crate::cli::command::CommandResponse for ResponseManifest {
183    fn into_mcp(self) -> crate::cli::command::McpResponseItem {
184        crate::cli::command::McpResponseItem::JSONL(serde_json::to_value(self).unwrap())
185    }
186}
187
188pub mod request_schema;
189
190
191pub mod response_schema;