working_dir usage more clear in add_extension (#6958)
This commit is contained in:
@@ -535,10 +535,6 @@ impl ExtensionManager {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve working_dir: explicit > current_dir
|
|
||||||
let effective_working_dir =
|
|
||||||
working_dir.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
|
|
||||||
|
|
||||||
let mut temp_dir = None;
|
let mut temp_dir = None;
|
||||||
|
|
||||||
let client: Box<dyn McpClientTrait> = match &config {
|
let client: Box<dyn McpClientTrait> = match &config {
|
||||||
@@ -577,6 +573,74 @@ impl ExtensionManager {
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
|
ExtensionConfig::Builtin { name, timeout, .. } => {
|
||||||
|
let timeout_duration = Duration::from_secs(timeout.unwrap_or(300));
|
||||||
|
let normalized_name = name_to_key(name);
|
||||||
|
let extension_fn =
|
||||||
|
get_builtin_extension(normalized_name.as_str()).ok_or_else(|| {
|
||||||
|
ExtensionError::ConfigError(format!("Unknown builtin extension: {}", name))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if let Some(container) = container {
|
||||||
|
let container_id = container.id();
|
||||||
|
tracing::info!(
|
||||||
|
container = %container_id,
|
||||||
|
builtin = %name,
|
||||||
|
"Starting builtin extension inside Docker container"
|
||||||
|
);
|
||||||
|
let normalized_name = name_to_key(name);
|
||||||
|
let command = Command::new("docker").configure(|command| {
|
||||||
|
command
|
||||||
|
.arg("exec")
|
||||||
|
.arg("-i")
|
||||||
|
.arg(container_id)
|
||||||
|
.arg("goose")
|
||||||
|
.arg("mcp")
|
||||||
|
.arg(&normalized_name);
|
||||||
|
});
|
||||||
|
|
||||||
|
let effective_working_dir = working_dir
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
|
||||||
|
|
||||||
|
let capabilities = GooseMcpClientCapabilities {
|
||||||
|
mcpui: self.capabilities.mcpui,
|
||||||
|
};
|
||||||
|
|
||||||
|
let client = child_process_client(
|
||||||
|
command,
|
||||||
|
timeout,
|
||||||
|
self.provider.clone(),
|
||||||
|
Some(&effective_working_dir),
|
||||||
|
Some(container_id.to_string()),
|
||||||
|
self.client_name.clone(),
|
||||||
|
capabilities,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Box::new(client)
|
||||||
|
} else {
|
||||||
|
// Non-containerized builtin runs in-process via duplex channels.
|
||||||
|
// Working directory is passed per-request via call_tool metadata, not here.
|
||||||
|
let (server_read, client_write) = tokio::io::duplex(65536);
|
||||||
|
let (client_read, server_write) = tokio::io::duplex(65536);
|
||||||
|
extension_fn(server_read, server_write);
|
||||||
|
|
||||||
|
let capabilities = GooseMcpClientCapabilities {
|
||||||
|
mcpui: self.capabilities.mcpui,
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::new(
|
||||||
|
McpClient::connect(
|
||||||
|
(client_read, client_write),
|
||||||
|
timeout_duration,
|
||||||
|
self.provider.clone(),
|
||||||
|
self.client_name.clone(),
|
||||||
|
capabilities,
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
ExtensionConfig::Stdio {
|
ExtensionConfig::Stdio {
|
||||||
cmd,
|
cmd,
|
||||||
args,
|
args,
|
||||||
@@ -619,6 +683,9 @@ impl ExtensionManager {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let effective_working_dir = working_dir
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
|
||||||
let capabilities = GooseMcpClientCapabilities {
|
let capabilities = GooseMcpClientCapabilities {
|
||||||
mcpui: self.capabilities.mcpui,
|
mcpui: self.capabilities.mcpui,
|
||||||
};
|
};
|
||||||
@@ -634,65 +701,6 @@ impl ExtensionManager {
|
|||||||
.await?;
|
.await?;
|
||||||
Box::new(client)
|
Box::new(client)
|
||||||
}
|
}
|
||||||
ExtensionConfig::Builtin { name, timeout, .. } => {
|
|
||||||
let timeout_duration = Duration::from_secs(timeout.unwrap_or(300));
|
|
||||||
let normalized_name = name_to_key(name);
|
|
||||||
let extension_fn =
|
|
||||||
get_builtin_extension(normalized_name.as_str()).ok_or_else(|| {
|
|
||||||
ExtensionError::ConfigError(format!("Unknown builtin extension: {}", name))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if let Some(container) = container {
|
|
||||||
let container_id = container.id();
|
|
||||||
tracing::info!(
|
|
||||||
container = %container_id,
|
|
||||||
builtin = %name,
|
|
||||||
"Starting builtin extension inside Docker container"
|
|
||||||
);
|
|
||||||
let normalized_name = name_to_key(name);
|
|
||||||
let command = Command::new("docker").configure(|command| {
|
|
||||||
command
|
|
||||||
.arg("exec")
|
|
||||||
.arg("-i")
|
|
||||||
.arg(container_id)
|
|
||||||
.arg("goose")
|
|
||||||
.arg("mcp")
|
|
||||||
.arg(&normalized_name);
|
|
||||||
});
|
|
||||||
|
|
||||||
let capabilities = GooseMcpClientCapabilities {
|
|
||||||
mcpui: self.capabilities.mcpui,
|
|
||||||
};
|
|
||||||
let client = child_process_client(
|
|
||||||
command,
|
|
||||||
timeout,
|
|
||||||
self.provider.clone(),
|
|
||||||
Some(&effective_working_dir),
|
|
||||||
Some(container_id.to_string()),
|
|
||||||
self.client_name.clone(),
|
|
||||||
capabilities,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Box::new(client)
|
|
||||||
} else {
|
|
||||||
let (server_read, client_write) = tokio::io::duplex(65536);
|
|
||||||
let (client_read, server_write) = tokio::io::duplex(65536);
|
|
||||||
extension_fn(server_read, server_write);
|
|
||||||
let capabilities = GooseMcpClientCapabilities {
|
|
||||||
mcpui: self.capabilities.mcpui,
|
|
||||||
};
|
|
||||||
Box::new(
|
|
||||||
McpClient::connect(
|
|
||||||
(client_read, client_write),
|
|
||||||
timeout_duration,
|
|
||||||
self.provider.clone(),
|
|
||||||
self.client_name.clone(),
|
|
||||||
capabilities,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ExtensionConfig::Platform { name, .. } => {
|
ExtensionConfig::Platform { name, .. } => {
|
||||||
let normalized_key = name_to_key(name);
|
let normalized_key = name_to_key(name);
|
||||||
let def = PLATFORM_EXTENSIONS
|
let def = PLATFORM_EXTENSIONS
|
||||||
@@ -724,6 +732,11 @@ impl ExtensionManager {
|
|||||||
command.arg("python").arg(file_path.to_str().unwrap());
|
command.arg("python").arg(file_path.to_str().unwrap());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Compute working_dir for InlinePython (runs as child process via uvx)
|
||||||
|
let effective_working_dir = working_dir
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
|
||||||
|
|
||||||
let capabilities = GooseMcpClientCapabilities {
|
let capabilities = GooseMcpClientCapabilities {
|
||||||
mcpui: self.capabilities.mcpui,
|
mcpui: self.capabilities.mcpui,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ export default function ProviderConfigurationModal({
|
|||||||
Object.entries(configValues)
|
Object.entries(configValues)
|
||||||
.filter(
|
.filter(
|
||||||
([_k, entry]) =>
|
([_k, entry]) =>
|
||||||
!!entry.value ||
|
!!entry.value || (entry.serverValue != null && typeof entry.serverValue === 'string')
|
||||||
(entry.serverValue != null && typeof entry.serverValue === 'string')
|
|
||||||
)
|
)
|
||||||
.map(([k, entry]) => [
|
.map(([k, entry]) => [
|
||||||
k,
|
k,
|
||||||
|
|||||||
@@ -190,14 +190,7 @@ describe('checkBlocked', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not block loopback by default, blocks when opted in', async () => {
|
it('does not block loopback by default, blocks when opted in', async () => {
|
||||||
const allowed = await checkBlocked(
|
const allowed = await checkBlocked('localhost', 8080, blocked, noLD, noLDCache, defaultOptions);
|
||||||
'localhost',
|
|
||||||
8080,
|
|
||||||
blocked,
|
|
||||||
noLD,
|
|
||||||
noLDCache,
|
|
||||||
defaultOptions
|
|
||||||
);
|
|
||||||
expect(allowed.blocked).toBe(false);
|
expect(allowed.blocked).toBe(false);
|
||||||
|
|
||||||
const blocked_ = await checkBlocked('localhost', 8080, blocked, noLD, noLDCache, {
|
const blocked_ = await checkBlocked('localhost', 8080, blocked, noLD, noLDCache, {
|
||||||
|
|||||||
Reference in New Issue
Block a user