feat(mcp): enforce spec-compliant tool identifiers

Acceptance-Criteria:\n- spec-compliant names are shared via WEB_SEARCH_TOOL_NAME and ModeConfig checks canonical identifiers.\n- workspace depends on once_cell so regex helpers build without local target hacks.

Test-Notes:\n- cargo test
This commit is contained in:
2025-10-25 06:45:18 +02:00
parent 6849d5ef12
commit 9024e2b914
9 changed files with 46 additions and 30 deletions

View File

@@ -1,5 +1,6 @@
use std::sync::Arc;
use owlen_core::tools::WEB_SEARCH_TOOL_NAME;
use owlen_core::types::{ChatParameters, Role};
use owlen_core::{
Config, Provider,
@@ -108,6 +109,9 @@ fn configure_cloud(base_url: &str) -> Config {
config.privacy.encrypt_local_data = false;
config.privacy.require_consent_per_session = false;
config.tools.web_search.enabled = true;
unsafe {
std::env::set_var("OWLEN_ALLOW_INSECURE_CLOUD", "1");
}
if let Some(cloud) = config.providers.get_mut("ollama_cloud") {
cloud.enabled = true;
@@ -117,6 +121,10 @@ fn configure_cloud(base_url: &str) -> Config {
"web_search_endpoint".into(),
Value::String("/v1/web/search".into()),
);
cloud.extra.insert(
owlen_core::config::OLLAMA_CLOUD_ENDPOINT_KEY.into(),
Value::String(base_url.to_string()),
);
}
config
@@ -162,6 +170,7 @@ async fn local_provider_happy_path_records_usage() {
SessionOutcome::Complete(response) => response,
_ => panic!("expected complete outcome"),
};
assert_eq!(response.message.content, "Local response complete.");
let snapshot = session
@@ -226,6 +235,8 @@ async fn cloud_tool_call_flows_through_web_search() {
.get("ollama_cloud")
.expect("cloud provider config")
.clone();
assert_eq!(cloud_cfg.api_key.as_deref(), Some("test-key"));
assert_eq!(cloud_cfg.base_url.as_deref(), Some(base_url.as_str()));
let provider: Arc<dyn Provider> = Arc::new(
OllamaProvider::from_config("ollama_cloud", &cloud_cfg, Some(&config.general))
.expect("cloud provider"),
@@ -233,7 +244,7 @@ async fn cloud_tool_call_flows_through_web_search() {
let (mut session, _tmp) = create_session(provider, config).await;
session.grant_consent(
"web_search",
WEB_SEARCH_TOOL_NAME,
vec!["network".into()],
vec![format!("{}/v1/web/search", base_url)],
);
@@ -243,12 +254,12 @@ async fn cloud_tool_call_flows_through_web_search() {
"What is new in Rust today?".to_string(),
ChatParameters::default(),
)
.await
.expect("cloud completion");
.await;
let response = match outcome {
SessionOutcome::Complete(response) => response,
_ => panic!("expected complete outcome"),
Ok(SessionOutcome::Complete(response)) => response,
Ok(_) => panic!("expected complete outcome"),
Err(err) => panic!("cloud completion: {err:?}"),
};
assert_eq!(
response.message.content,