fix(application,domain,backend): les serveurs MCP des plugins assignés atteignent la session de l'agent — #302 (cause: MutateAgentPluginTool persistait bien PluginToolRef dans ProjectPluginSettings mais le launcher ne consommait jamais assignments.tools, les configs de session ne contenaient que le serveur MCP natif idea, donc tool_search restait aveugle aux tools plugin comme unity_detect_project; fix: ProjectPluginResolver::assigned_mcp_servers projette les affectations de l'agent en specs stdio uniquement si plugin activé pour le projet/runtime-actif/installé et serveur autoStart, avec substitution ${pluginRoot}/${appDataDir}, dédup par (pluginId,serverId), et nom client stable idea_plugin__<enc>__<enc> via PluginMcpServerSpec::client_name qui encode chaque octet non alphanumérique pour éviter collisions TOML/namespace; matérialisation au lancement/restart dans .mcp.json + enabledMcpjsonServers (Claude), mcp_servers de .codex/config.toml (Codex), bloc mcp de opencode.json (OpenCode local/cloud), avec réconciliation préservant le wiring idea réel lors d'un restart interne sans McpRuntime et retirant les entrées plugin obsolètes; racines cwd/commande des serveurs assignés ajoutées en lecture au plan Landlock quand un plan existe; aucun changement de .ideai/mcp-tool-permissions.json ni pollution du catalogue natif idea_*; gap documenté: OpenAiCompatibleSession/tools_for_context ne rend pas les tools plugin appelables en direct (nécessite client JSON-RPC dédié), à traiter séparément; QA verte reconfirmée par Git: rustup run stable cargo fmt --all -- --check, git diff --check, cargo test -p application --lib 180/180 dont assigned_mcp_servers 2/2, cargo test -p application --test agent_lifecycle 111/111, cargo test -p domain plugin --lib 7/7 dont plugin_mcp_client_name_is_stable_and_collision_free_for_punctuation, cargo check -p backend OK, cargo check -p app-tauri OK; fichiers tickets/296/*, tickets/299-302/*, counter/index, attachments/ et scripts/ hors périmètre; aucun push)

This commit is contained in:
2026-09-09 23:37:32 +02:00
parent 0cfbc064b6
commit 78b379ac51
4 changed files with 906 additions and 40 deletions

View File

@ -796,6 +796,36 @@ pub struct PluginMcpServerSpec {
pub transport: String,
}
impl PluginMcpServerSpec {
/// Stable, collision-free MCP client name for this plugin server.
///
/// Plugin/server ids may contain punctuation that is awkward in TOML table
/// paths and becomes part of the tool namespace in MCP clients. Encode every
/// non-alphanumeric byte instead of merely replacing it, so distinct ids can
/// never collapse onto the same session server name.
#[must_use]
pub fn client_name(&self) -> String {
fn component(raw: &str) -> String {
let mut out = String::new();
for byte in raw.bytes() {
if byte.is_ascii_alphanumeric() {
out.push(char::from(byte).to_ascii_lowercase());
} else {
out.push('_');
out.push_str(&format!("{byte:02x}"));
}
}
out
}
format!(
"idea_plugin__{}__{}",
component(self.plugin_id.as_str()),
component(self.server_id.as_str())
)
}
}
/// MCP server status.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -862,6 +892,32 @@ mod tests {
assert!(PluginLifecycleState::Enabled.is_runtime_active());
}
#[test]
fn plugin_mcp_client_name_is_stable_and_collision_free_for_punctuation() {
fn spec(plugin: &str) -> PluginMcpServerSpec {
PluginMcpServerSpec {
plugin_id: PluginId::new(plugin).unwrap(),
server_id: PluginMcpServerId::new("unity-editor").unwrap(),
identity: String::new(),
display_name: String::new(),
command: String::new(),
args: Vec::new(),
env: Vec::new(),
cwd: String::new(),
transport: "stdio".to_owned(),
}
}
assert_eq!(
spec("com.idea.unity").client_name(),
"idea_plugin__com_2eidea_2eunity__unity_2deditor"
);
assert_ne!(
spec("com.idea-unity").client_name(),
spec("com.idea.unity").client_name()
);
}
#[test]
fn plugin_capabilities_serialize_public_manifest_names() {
assert_eq!(