rust acp client for extension methods (#8227)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -47,6 +47,7 @@ http-body-util = "0.1.3"
|
||||
uuid = { workspace = true, features = ["v7"] }
|
||||
schemars = { workspace = true, features = ["derive"] }
|
||||
goose-acp-macros = { path = "../goose-acp-macros" }
|
||||
goose-sdk = { path = "../goose-sdk" }
|
||||
|
||||
[dev-dependencies]
|
||||
async-trait = { workspace = true }
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
},
|
||||
{
|
||||
"method": "_goose/config/extensions",
|
||||
"requestType": null,
|
||||
"requestType": "GetExtensionsRequest",
|
||||
"responseType": "GetExtensionsResponse"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
"type": "string"
|
||||
},
|
||||
"config": {
|
||||
"description": "Extension configuration (see ExtensionConfig variants: Stdio, StreamableHttp, Builtin, Platform)."
|
||||
"description": "Extension configuration (see ExtensionConfig variants: Stdio, StreamableHttp, Builtin, Platform).",
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionId",
|
||||
"config"
|
||||
"sessionId"
|
||||
],
|
||||
"description": "Add an extension to an active session.",
|
||||
"x-side": "agent",
|
||||
@@ -69,6 +69,7 @@
|
||||
"required": [
|
||||
"tools"
|
||||
],
|
||||
"description": "Tools response.",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/tools"
|
||||
},
|
||||
@@ -98,12 +99,11 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"description": "The resource result from the extension (MCP ReadResourceResult)."
|
||||
"description": "The resource result from the extension (MCP ReadResourceResult).",
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"description": "Resource read response.",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/resource/read"
|
||||
},
|
||||
@@ -147,12 +147,10 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"session": {
|
||||
"description": "The session object with id, name, working_dir, timestamps, tokens, etc."
|
||||
"description": "The session object with id, name, working_dir, timestamps, tokens, etc.",
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"session"
|
||||
],
|
||||
"description": "Get a session response.",
|
||||
"x-side": "agent",
|
||||
"x-method": "session/get"
|
||||
@@ -195,6 +193,7 @@
|
||||
"required": [
|
||||
"data"
|
||||
],
|
||||
"description": "Export session response.",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/session/export"
|
||||
},
|
||||
@@ -216,15 +215,20 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"session": {
|
||||
"description": "The imported session object."
|
||||
"description": "The imported session object.",
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"session"
|
||||
],
|
||||
"description": "Import session response.",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/session/import"
|
||||
},
|
||||
"GetExtensionsRequest": {
|
||||
"type": "object",
|
||||
"description": "List configured extensions and any warnings.",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/config/extensions"
|
||||
},
|
||||
"GetExtensionsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -340,6 +344,15 @@
|
||||
],
|
||||
"description": "Params for _goose/session/import",
|
||||
"title": "ImportSessionRequest"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/GetExtensionsRequest"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/config/extensions",
|
||||
"title": "GetExtensionsRequest"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Schema descriptor for a single custom method, produced by the
|
||||
/// `#[custom_methods]` macro's generated `custom_method_schemas()` function.
|
||||
///
|
||||
/// `params_schema` / `response_schema` hold `$ref` pointers or inline schemas
|
||||
/// produced by `SchemaGenerator::subschema_for`. All referenced types are
|
||||
/// collected in the generator's `$defs` map.
|
||||
///
|
||||
/// `params_type_name` / `response_type_name` carry the Rust struct name so the
|
||||
/// binary can key `$defs` entries and annotate them with `x-method` / `x-side`.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct CustomMethodSchema {
|
||||
pub method: String,
|
||||
pub params_schema: Option<schemars::Schema>,
|
||||
pub params_type_name: Option<String>,
|
||||
pub response_schema: Option<schemars::Schema>,
|
||||
pub response_type_name: Option<String>,
|
||||
}
|
||||
|
||||
/// Add an extension to an active session.
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AddExtensionRequest {
|
||||
pub session_id: String,
|
||||
/// Extension configuration (see ExtensionConfig variants: Stdio, StreamableHttp, Builtin, Platform).
|
||||
pub config: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Remove an extension from an active session.
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoveExtensionRequest {
|
||||
pub session_id: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// List all tools available in a session.
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetToolsRequest {
|
||||
pub session_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, JsonSchema)]
|
||||
pub struct GetToolsResponse {
|
||||
/// Array of tool info objects with `name`, `description`, `parameters`, and optional `permission`.
|
||||
pub tools: Vec<serde_json::Value>,
|
||||
}
|
||||
|
||||
/// Read a resource from an extension.
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ReadResourceRequest {
|
||||
pub session_id: String,
|
||||
pub uri: String,
|
||||
pub extension_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, JsonSchema)]
|
||||
pub struct ReadResourceResponse {
|
||||
/// The resource result from the extension (MCP ReadResourceResult).
|
||||
pub result: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Update the working directory for a session.
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateWorkingDirRequest {
|
||||
pub session_id: String,
|
||||
pub working_dir: String,
|
||||
}
|
||||
|
||||
/// Get a session by ID.
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetSessionRequest {
|
||||
pub session_id: String,
|
||||
#[serde(default)]
|
||||
pub include_messages: bool,
|
||||
}
|
||||
|
||||
/// Get a session response.
|
||||
#[derive(Debug, Serialize, JsonSchema)]
|
||||
pub struct GetSessionResponse {
|
||||
/// The session object with id, name, working_dir, timestamps, tokens, etc.
|
||||
pub session: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Delete a session.
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DeleteSessionRequest {
|
||||
pub session_id: String,
|
||||
}
|
||||
|
||||
/// Export a session as a JSON string.
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExportSessionRequest {
|
||||
pub session_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, JsonSchema)]
|
||||
pub struct ExportSessionResponse {
|
||||
pub data: String,
|
||||
}
|
||||
|
||||
/// Import a session from a JSON string.
|
||||
#[derive(Debug, Deserialize, JsonSchema)]
|
||||
pub struct ImportSessionRequest {
|
||||
pub data: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, JsonSchema)]
|
||||
pub struct ImportSessionResponse {
|
||||
/// The imported session object.
|
||||
pub session: serde_json::Value,
|
||||
}
|
||||
|
||||
/// List configured extensions and any warnings.
|
||||
#[derive(Debug, Serialize, JsonSchema)]
|
||||
pub struct GetExtensionsResponse {
|
||||
/// Array of ExtensionEntry objects with `enabled` flag and config details.
|
||||
pub extensions: Vec<serde_json::Value>,
|
||||
pub warnings: Vec<String>,
|
||||
}
|
||||
|
||||
/// Empty success response for operations that return no data.
|
||||
#[derive(Debug, Serialize, JsonSchema)]
|
||||
pub struct EmptyResponse {}
|
||||
@@ -1,7 +1,7 @@
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
mod adapters;
|
||||
pub mod custom_requests;
|
||||
pub use goose_sdk::custom_requests;
|
||||
mod fs;
|
||||
pub mod server;
|
||||
pub mod server_factory;
|
||||
|
||||
@@ -1303,7 +1303,7 @@ impl GooseAcpAgent {
|
||||
|
||||
#[custom_methods]
|
||||
impl GooseAcpAgent {
|
||||
#[custom_method("_goose/extensions/add")]
|
||||
#[custom_method(AddExtensionRequest)]
|
||||
async fn on_add_extension(
|
||||
&self,
|
||||
req: AddExtensionRequest,
|
||||
@@ -1318,7 +1318,7 @@ impl GooseAcpAgent {
|
||||
Ok(EmptyResponse {})
|
||||
}
|
||||
|
||||
#[custom_method("_goose/extensions/remove")]
|
||||
#[custom_method(RemoveExtensionRequest)]
|
||||
async fn on_remove_extension(
|
||||
&self,
|
||||
req: RemoveExtensionRequest,
|
||||
@@ -1331,7 +1331,7 @@ impl GooseAcpAgent {
|
||||
Ok(EmptyResponse {})
|
||||
}
|
||||
|
||||
#[custom_method("_goose/tools")]
|
||||
#[custom_method(GetToolsRequest)]
|
||||
async fn on_get_tools(&self, req: GetToolsRequest) -> Result<GetToolsResponse, sacp::Error> {
|
||||
let agent = self.get_session_agent(&req.session_id, None).await?;
|
||||
let tools = agent.list_tools(&req.session_id, None).await;
|
||||
@@ -1343,7 +1343,7 @@ impl GooseAcpAgent {
|
||||
Ok(GetToolsResponse { tools: tools_json })
|
||||
}
|
||||
|
||||
#[custom_method("_goose/resource/read")]
|
||||
#[custom_method(ReadResourceRequest)]
|
||||
async fn on_read_resource(
|
||||
&self,
|
||||
req: ReadResourceRequest,
|
||||
@@ -1362,7 +1362,7 @@ impl GooseAcpAgent {
|
||||
})
|
||||
}
|
||||
|
||||
#[custom_method("_goose/working_dir/update")]
|
||||
#[custom_method(UpdateWorkingDirRequest)]
|
||||
async fn on_update_working_dir(
|
||||
&self,
|
||||
req: UpdateWorkingDirRequest,
|
||||
@@ -1394,8 +1394,7 @@ impl GooseAcpAgent {
|
||||
Ok(EmptyResponse {})
|
||||
}
|
||||
|
||||
// TODO: use typed GetSessionRequest when agent-client-protocol-schema adds it (Discussion #60)
|
||||
#[custom_method("session/get")]
|
||||
#[custom_method(GetSessionRequest)]
|
||||
async fn on_get_session(
|
||||
&self,
|
||||
req: GetSessionRequest,
|
||||
@@ -1412,8 +1411,7 @@ impl GooseAcpAgent {
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: use typed DeleteSessionRequest when agent-client-protocol-schema adds it (RFD #395)
|
||||
#[custom_method("session/delete")]
|
||||
#[custom_method(DeleteSessionRequest)]
|
||||
async fn on_delete_session(
|
||||
&self,
|
||||
req: DeleteSessionRequest,
|
||||
@@ -1426,7 +1424,7 @@ impl GooseAcpAgent {
|
||||
Ok(EmptyResponse {})
|
||||
}
|
||||
|
||||
#[custom_method("_goose/session/export")]
|
||||
#[custom_method(ExportSessionRequest)]
|
||||
async fn on_export_session(
|
||||
&self,
|
||||
req: ExportSessionRequest,
|
||||
@@ -1439,7 +1437,7 @@ impl GooseAcpAgent {
|
||||
Ok(ExportSessionResponse { data })
|
||||
}
|
||||
|
||||
#[custom_method("_goose/session/import")]
|
||||
#[custom_method(ImportSessionRequest)]
|
||||
async fn on_import_session(
|
||||
&self,
|
||||
req: ImportSessionRequest,
|
||||
@@ -1456,7 +1454,7 @@ impl GooseAcpAgent {
|
||||
})
|
||||
}
|
||||
|
||||
#[custom_method("_goose/config/extensions")]
|
||||
#[custom_method(GetExtensionsRequest)]
|
||||
async fn on_get_extensions(&self) -> Result<GetExtensionsResponse, sacp::Error> {
|
||||
let extensions = goose::config::extensions::get_all_extensions();
|
||||
let warnings = goose::config::extensions::get_warnings();
|
||||
|
||||
Reference in New Issue
Block a user