Skip to main content

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

1//! `tools 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.tools.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.tools.get.Path")]
18pub enum Path {
19    #[serde(rename = "tools/get")]
20    ToolsGet,
21}
22
23impl CommandRequest for Request {
24    fn request_base(&self) -> &crate::cli::command::RequestBase {
25        &self.base
26    }
27
28    fn request_base_mut(&mut self) -> Option<&mut crate::cli::command::RequestBase> {
29        Some(&mut self.base)
30    }
31}
32
33/// Per-OS exec command for a tool. The current platform's vector is
34/// the program plus its leading arguments; the caller's `--args` are
35/// appended, and the result runs with CWD = the tool's version folder's
36/// `cli/` subdir (`objectiveai.json` lives in the version folder).
37#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
38#[schemars(rename = "cli.command.tools.get.Exec")]
39pub struct Exec {
40    pub windows: Vec<String>,
41    pub linux: Vec<String>,
42    pub macos: Vec<String>,
43}
44
45impl Exec {
46    pub fn is_empty(&self) -> bool {
47        self.windows.is_empty() && self.linux.is_empty() && self.macos.is_empty()
48    }
49}
50
51/// Wire response for `tools get` — a lean projection of the on-disk
52/// manifest. `exec` is required (a tool always has a command). The
53/// on-disk-only fields (`cli_zip`, `source`) are intentionally absent;
54/// the CLI owns the full on-disk shape in its own `filesystem::tools`
55/// manifest types.
56#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
57#[schemars(rename = "cli.command.tools.get.ResponseManifest")]
58pub struct ResponseManifest {
59    pub owner: String,
60    pub name: String,
61    pub version: String,
62    pub description: String,
63    pub exec: Exec,
64}
65
66impl ResponseManifest {
67    /// LLM-visible MCP tool name for this tool:
68    /// `tool_{owner}_{name}_{version}`, with every `.` in the version
69    /// substituted to `-` so the result stays within the Anthropic
70    /// tool-name regex (`^[a-zA-Z0-9_-]{1,128}$`). `objectiveai-mcp`
71    /// advertises each tool under this name.
72    pub fn tool_name(&self) -> String {
73        format!(
74            "tool_{}_{}_{}",
75            self.owner,
76            self.name,
77            self.version.replace('.', "-")
78        )
79    }
80}
81
82pub type Response = Option<ResponseManifest>;
83
84#[derive(clap::Args)]
85#[command(group(clap::ArgGroup::new("owner_required").required(true).args(["owner"])))]
86#[command(group(clap::ArgGroup::new("name_required").required(true).args(["name"])))]
87#[command(group(clap::ArgGroup::new("version_required").required(true).args(["version"])))]
88pub struct Args {
89    /// Tool owner (GitHub `<owner>` segment). Required.
90    #[arg(long)]
91    pub owner: Option<String>,
92    /// Tool name (repository segment). Required.
93    #[arg(long)]
94    pub name: Option<String>,
95    /// Tool version. Required.
96    #[arg(long)]
97    pub version: Option<String>,
98    #[command(flatten)]
99    pub base: crate::cli::command::RequestBaseArgs,
100}
101
102#[derive(clap::Args)]
103#[command(args_conflicts_with_subcommands = true)]
104pub struct Command {
105    #[command(flatten)]
106    pub args: Args,
107    #[command(subcommand)]
108    pub schema: Option<Schema>,
109}
110
111#[derive(clap::Subcommand)]
112pub enum Schema {
113    /// Emit the JSON Schema for this leaf's `Request` type and exit.
114    RequestSchema(request_schema::Args),
115    /// Emit the JSON Schema for this leaf's `Response` type and exit.
116    ResponseSchema(response_schema::Args),
117}
118
119impl TryFrom<Args> for Request {
120    type Error = crate::cli::command::FromArgsError;
121    fn try_from(args: Args) -> Result<Self, Self::Error> {
122        Ok(Self {
123            path_type: Path::ToolsGet,
124            owner: args.owner.ok_or_else(|| {
125                crate::cli::command::FromArgsError::path_parse(
126                    "owner",
127                    "--owner is required".to_string(),
128                )
129            })?,
130            name: args.name.ok_or_else(|| {
131                crate::cli::command::FromArgsError::path_parse(
132                    "name",
133                    "--name is required".to_string(),
134                )
135            })?,
136            version: args.version.ok_or_else(|| {
137                crate::cli::command::FromArgsError::path_parse(
138                    "version",
139                    "--version is required".to_string(),
140                )
141            })?,
142            base: args.base.into(),
143        })
144    }
145}
146
147#[cfg(feature = "cli-executor")]
148pub async fn execute<E: crate::cli::command::CommandExecutor>(
149    executor: &E,
150    mut request: Request,
151
152        agent_arguments: Option<&crate::cli::command::AgentArguments>,
153    ) -> Result<Response, E::Error> {
154    request.base.clear_transform();
155    executor.execute_one(request, agent_arguments).await
156}
157
158#[cfg(feature = "cli-executor")]
159pub async fn execute_transform<E: crate::cli::command::CommandExecutor>(
160    executor: &E,
161    mut request: Request,
162    transform: crate::cli::command::Transform,
163
164        agent_arguments: Option<&crate::cli::command::AgentArguments>,
165    ) -> Result<serde_json::Value, E::Error> {
166    request.base.set_transform(transform);
167    executor.execute_one(request, agent_arguments).await
168}
169
170#[cfg(feature = "mcp")]
171impl crate::cli::command::CommandResponse for ResponseManifest {
172    fn into_mcp(self) -> crate::cli::command::McpResponseItem {
173        crate::cli::command::McpResponseItem::JSONL(serde_json::to_value(self).unwrap())
174    }
175}
176
177pub mod request_schema;
178
179
180pub mod response_schema;