Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"!.pnpm-store",
"!sdk/src/generated",
"!src-tauri/gen",
"!src-tauri/testdata/goose/mcp_app_proxy_11deb564.html",
"!src-tauri/plugins/*/permissions/{schemas,autogenerated}",
"!.agents",
"!.worktrees",
Expand Down
1 change: 1 addition & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ tauri-build = { version = "2", features = [] }

[dependencies]
anyhow = "1"
axum = { version = "0.8", default-features = false, features = ["http1", "json", "tokio"] }
base64 = "0.22"
builderbot-auth = { path = "../crates/builderbot-auth", features = ["blocking-client"] }
bytes = "1"
Expand Down
36 changes: 5 additions & 31 deletions src-tauri/src/commands/acp.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
use std::env;

use crate::services::acp::GooseServeProcess;
use serde::Serialize;

const GOOSE_SERVE_URL_ENV: &str = "GOOSE_SERVE_URL";

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GooseServeHostInfo {
pub http_base_url: String,
pub secret_key: String,
}

#[tauri::command]
pub async fn get_goose_serve_url(app_handle: tauri::AppHandle) -> Result<String, String> {
if let Some(url) = configured_goose_serve_url() {
Expand All @@ -21,26 +13,6 @@ pub async fn get_goose_serve_url(app_handle: tauri::AppHandle) -> Result<String,
Ok(process.ws_url())
}

#[tauri::command]
pub async fn get_goose_serve_host_info(
app_handle: tauri::AppHandle,
) -> Result<GooseServeHostInfo, String> {
if let Some(url) = configured_goose_serve_url() {
let secret_key = goose_serve_url_token(&url)?;
ensure_configured_goose_serve_supports_inline_apps(&url)?;
return Ok(GooseServeHostInfo {
http_base_url: goose_serve_http_base_url(&url)?,
secret_key,
});
}

let process = GooseServeProcess::get(app_handle).await?;
Ok(GooseServeHostInfo {
http_base_url: process.http_base_url(),
secret_key: process.secret_key().to_string(),
})
}

fn configured_goose_serve_url() -> Option<String> {
env::var(GOOSE_SERVE_URL_ENV)
.ok()
Expand All @@ -59,7 +31,7 @@ fn parse_goose_serve_url(goose_serve_url: &str) -> Result<reqwest::Url, String>
.map_err(|error| format!("Invalid {GOOSE_SERVE_URL_ENV}: {error}"))
}

fn goose_serve_url_token(goose_serve_url: &str) -> Result<String, String> {
pub(crate) fn goose_serve_url_token(goose_serve_url: &str) -> Result<String, String> {
let parsed = parse_goose_serve_url(goose_serve_url)?;
parsed
.query_pairs()
Expand Down Expand Up @@ -90,7 +62,7 @@ fn missing_goose_serve_url_token_error() -> String {
format!("{GOOSE_SERVE_URL_ENV} must include a non-empty token query parameter")
}

fn goose_serve_http_base_url(goose_serve_url: &str) -> Result<String, String> {
pub(crate) fn goose_serve_http_base_url(goose_serve_url: &str) -> Result<String, String> {
let (scheme, rest) = goose_serve_url
.trim()
.split_once("://")
Expand Down Expand Up @@ -133,7 +105,9 @@ fn goose_serve_http_path_prefix(path: &str) -> String {
path.to_string()
}

fn ensure_configured_goose_serve_supports_inline_apps(goose_serve_url: &str) -> Result<(), String> {
pub(crate) fn ensure_configured_goose_serve_supports_inline_apps(
goose_serve_url: &str,
) -> Result<(), String> {
if !goose_serve_url_uses_plaintext_http(goose_serve_url)? {
return Err(format!(
"{GOOSE_SERVE_URL_ENV} must use ws or http for inline MCP apps because the app guest origin is served over local http"
Expand Down
12 changes: 10 additions & 2 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,15 @@ pub fn run() {
mode.log_enabled();
}

let trusted_mcp_sandbox_ipc = services::mcp_app_proxy::TrustedMcpSandboxIpc::new();
let trusted_mcp_sandbox_init = trusted_mcp_sandbox_ipc.initialization_script();
let builder = if let Some(mode) = e2e_mode {
builder.manage(mode)
} else {
builder
};
}
.append_invoke_initialization_script(trusted_mcp_sandbox_init)
.manage(trusted_mcp_sandbox_ipc);

builder
.setup(|app| {
Expand Down Expand Up @@ -211,6 +215,10 @@ pub fn run() {
app.manage(commands::pocket_voice::PocketVoiceState::default());
app.manage(commands::native_voice::NativeVoiceState::default());
app.manage(commands::voice_capture::VoiceCaptureState::default());
let mcp_app_proxy =
tauri::async_runtime::block_on(services::mcp_app_proxy::McpAppProxyServer::start())
.map_err(std::io::Error::other)?;
app.manage(mcp_app_proxy);
let release_channel_state = commands::updates::ReleaseChannelState::load(app.handle())?;
app.manage(release_channel_state);

Expand Down Expand Up @@ -485,7 +493,7 @@ pub fn run() {
commands::builderbot::update_builderbot_routing_rule,
commands::whoami::whoami,
commands::acp::get_goose_serve_url,
commands::acp::get_goose_serve_host_info,
services::mcp_app_proxy::create_mcp_app_sandbox,
commands::project_icons::scan_project_icons,
commands::project_icons::read_project_icon,
commands::renderer::log_renderer_event,
Expand Down
14 changes: 6 additions & 8 deletions src-tauri/src/services/acp/goose_serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,12 @@ impl GooseServeProcess {
acp_websocket_url(self.port, &self.secret_key)
}

/// Return the HTTP base URL for authenticated Goose server routes.
pub fn http_base_url(&self) -> String {
format!("http://{LOCALHOST}:{}", self.port)
}

/// Return the secret key used to authenticate local HTTP requests.
pub fn secret_key(&self) -> &str {
&self.secret_key
/// Return the HTTP base URL and secret for the in-process MCP sandbox proxy.
pub(crate) fn proxy_credentials(&self) -> (String, &str) {
(
format!("http://{LOCALHOST}:{}", self.port),
&self.secret_key,
)
}

/// Get a reference to the running process, or an error if it was never
Expand Down
Loading