fix(web): démarre le serveur MCP custom CLI via idea-serve au lieu de node — #224
This commit is contained in:
@ -3441,23 +3441,9 @@ fn opencode_config_json(
|
||||
}),
|
||||
);
|
||||
|
||||
let (command, args) = match runtime {
|
||||
Some(rt) => (
|
||||
rt.exe.clone(),
|
||||
vec![
|
||||
"mcp-server".to_owned(),
|
||||
"--endpoint".to_owned(),
|
||||
rt.endpoint.clone(),
|
||||
"--project".to_owned(),
|
||||
rt.project_id.clone(),
|
||||
"--requester".to_owned(),
|
||||
rt.requester.clone(),
|
||||
],
|
||||
),
|
||||
None => ("idea".to_owned(), vec!["mcp-server".to_owned()]),
|
||||
};
|
||||
let command_array = std::iter::once(command)
|
||||
.chain(args)
|
||||
let wiring = mcp_server_wiring(domain::profile::McpTransport::Stdio, runtime);
|
||||
let command_array = std::iter::once(wiring.command)
|
||||
.chain(wiring.args)
|
||||
.map(serde_json::Value::String)
|
||||
.collect::<Vec<_>>();
|
||||
root.insert(
|
||||
@ -5334,6 +5320,37 @@ command = "idea-mcp"
|
||||
assert!(body.get("permission").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opencode_config_json_with_runtime_uses_idea_exe_not_node() {
|
||||
let config =
|
||||
domain::profile::OpenCodeConfig::new("http://localhost:8080", None, "m", None, None)
|
||||
.unwrap();
|
||||
let runtime = McpRuntime {
|
||||
exe: "/opt/idea/idea-serve".to_owned(),
|
||||
endpoint: "/run/user/1000/idea-mcp/proj.sock".to_owned(),
|
||||
project_id: "0123456789abcdef0123456789abcdef".to_owned(),
|
||||
requester: "11112222-3333-4444-5555-666677778888".to_owned(),
|
||||
};
|
||||
|
||||
let body = opencode_config_json(&config, "/project", Some(&runtime), None);
|
||||
let command = body["mcp"]["idea"]["command"]
|
||||
.as_array()
|
||||
.expect("OpenCode local command is an argv array")
|
||||
.iter()
|
||||
.filter_map(serde_json::Value::as_str)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(command[0], "/opt/idea/idea-serve");
|
||||
assert_ne!(command[0], "node");
|
||||
assert_eq!(command[1], "mcp-server");
|
||||
assert!(command.contains(&"--endpoint"));
|
||||
assert!(command.contains(&"/run/user/1000/idea-mcp/proj.sock"));
|
||||
assert!(command.contains(&"--project"));
|
||||
assert!(command.contains(&"0123456789abcdef0123456789abcdef"));
|
||||
assert!(command.contains(&"--requester"));
|
||||
assert!(command.contains(&"11112222-3333-4444-5555-666677778888"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opencode_config_json_projects_allow_and_deny() {
|
||||
let config =
|
||||
|
||||
@ -2594,6 +2594,80 @@ async fn launch_opencode_writes_isolated_config_and_env() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn launch_opencode_chat_with_runtime_uses_idea_bridge_without_pty_or_node() {
|
||||
let profile = opencode_profile(pid(9));
|
||||
let (launch, agent, fs, pty, _bus, _sessions, tr, _session) = launch_fixture_with_profile(
|
||||
profile,
|
||||
Some(ContextInjectionPlan::File {
|
||||
target: "AGENTS.md".to_owned(),
|
||||
}),
|
||||
);
|
||||
let factory = FakeStructuredFactory::new(Arc::clone(&tr), sid(888));
|
||||
let structured = Arc::new(StructuredSessions::new());
|
||||
let launch = launch
|
||||
.with_structured_routing_mode(StructuredRoutingMode::HumanPtyFallback)
|
||||
.with_structured(Arc::new(factory.clone()), Arc::clone(&structured));
|
||||
let runtime = application::McpRuntime {
|
||||
exe: "/opt/idea/idea-serve".to_owned(),
|
||||
endpoint: "/run/user/1000/idea-mcp/proj.sock".to_owned(),
|
||||
project_id: "0123456789abcdef0123456789abcdef".to_owned(),
|
||||
requester: agent.id.to_string(),
|
||||
};
|
||||
let mut input = launch_input_with_runtime(agent.id, runtime);
|
||||
input.require_structured = true;
|
||||
input.node_id = Some(nid(224));
|
||||
|
||||
let out = launch.execute(input).await.unwrap();
|
||||
|
||||
assert_eq!(factory.starts(), vec![pid(9)]);
|
||||
assert!(
|
||||
pty.spawns().is_empty(),
|
||||
"web custom chat must stay structured"
|
||||
);
|
||||
assert!(
|
||||
out.structured.is_some(),
|
||||
"backend returns a chat descriptor"
|
||||
);
|
||||
assert_eq!(
|
||||
structured.node_for_agent_in_project(project().id, &agent.id),
|
||||
Some(nid(224))
|
||||
);
|
||||
|
||||
let run_dir = format!("/home/me/proj/.ideai/run/{}", agent.id);
|
||||
let opencode = writes_ending_with(&fs, "/opencode.json");
|
||||
assert_eq!(opencode.len(), 1, "one OpenCode config is written");
|
||||
let body: serde_json::Value = serde_json::from_slice(&opencode[0].1).unwrap();
|
||||
let command = body["mcp"]["idea"]["command"]
|
||||
.as_array()
|
||||
.expect("OpenCode local command is an argv array")
|
||||
.iter()
|
||||
.filter_map(serde_json::Value::as_str)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(command[0], "/opt/idea/idea-serve");
|
||||
assert_ne!(command[0], "node");
|
||||
assert_eq!(command[1], "mcp-server");
|
||||
assert!(command.contains(&"--endpoint"));
|
||||
assert!(command.contains(&"/run/user/1000/idea-mcp/proj.sock"));
|
||||
assert!(command.contains(&"--project"));
|
||||
assert!(command.contains(&"0123456789abcdef0123456789abcdef"));
|
||||
assert!(command.contains(&"--requester"));
|
||||
let requester = agent.id.to_string();
|
||||
assert!(command.contains(&requester.as_str()));
|
||||
|
||||
let env: std::collections::HashMap<_, _> = factory
|
||||
.envs()
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("structured start receives env")
|
||||
.into_iter()
|
||||
.collect();
|
||||
assert_eq!(
|
||||
env.get("OPENCODE_CONFIG").map(String::as_str),
|
||||
Some(format!("{run_dir}/opencode.json").as_str())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn launch_mcp_flag_appends_flag_and_path_to_args() {
|
||||
// Test 5 — Flag { flag: "--mcp-config" } ⇒ spec.args carries the flag followed
|
||||
|
||||
Reference in New Issue
Block a user