feat(agents,plugins): surface unifiée Capacités de l'agent — skills et tools MCP natifs + plugins #296 (domain: PluginMcpToolMetadata dans PluginMcpServerContribution, validation hôte alignée SDK — name requis non vide, description optionnelle non vide, doublons rejetés; application: McpToolPermissionCatalogue.vue structurée additive tools {name, description, access read|writeAction} enrichie des descriptions canoniques infrastructure orchestrator mcp tools, use case SetAgentMcpToolAllowed — toggle atomique idempotente d'un tool natif dans l'allowlist explicite d'un agent, refus unknown tool sans mutation, overrides vides retirés; backend DTO: catalogue.tools additive + SetAgentMcpToolAllowedRequestDto, wire camelCase; app-tauri + web-server: commande set_agent_mcp_tool_allowed; frontend: AgentPluginCapabilities devient la surface unifiée — skills natifs assign/unassign, tools natifs via setAgentMcpToolAllowed (catalogue structuré avec fallback legacy), skills/tools plugins avec metadata statiques mcpServers[].tools et saisie manuelle fallback pour serveurs sans catalogue, tombstones indisponibles, bannière redémarrage requis si agent actif; AgentsPanel retire l'ancienne section Skills séparée au profit de la surface unifiée; ProjectPluginsPanel recentré enable/disable avec renvoi Capacités des agents; adapters Tauri/HTTP/mock + permission.test.ts; QA verte reconfirmée par Git: application mcp_tool_permissions 5/5 + plugin::tests 37/37, backend ciblés 3/3 + checks domain/application/backend, app-tauri dto 33/33 + dto_agents 30/30 + dto_plugins 2/2, check web-server, npm test 35/35 + 65/65, npm run build; suites complètes rouges classées non bloquantes par QA: web-server parallèle too many open files + 7 PTY série passant individuellement, frontend 32 échecs sur 6 fichiers web non modifiés reproduits isolément)
This commit is contained in:
@ -2952,6 +2952,20 @@ pub struct McpToolCatalogueDto {
|
||||
pub read_only_tools: Vec<String>,
|
||||
/// Tools treated as writing/action/execution tools.
|
||||
pub write_action_tools: Vec<String>,
|
||||
/// Structured additive catalogue for unified capability surfaces.
|
||||
pub tools: Vec<McpToolCatalogueEntryDto>,
|
||||
}
|
||||
|
||||
/// Structured metadata for one native IdeA MCP tool.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct McpToolCatalogueEntryDto {
|
||||
/// Stable tool name.
|
||||
pub name: String,
|
||||
/// Human-readable description from the canonical MCP definition.
|
||||
pub description: String,
|
||||
/// Coarse access class: `read` or `writeAction`.
|
||||
pub access: String,
|
||||
}
|
||||
|
||||
/// Full MCP tool permission state crossing the wire.
|
||||
@ -2975,6 +2989,20 @@ impl From<ReadMcpToolPermissionsOutput> for ProjectMcpToolPermissionsDto {
|
||||
catalogue: McpToolCatalogueDto {
|
||||
read_only_tools: out.catalogue.read_only_tools,
|
||||
write_action_tools: out.catalogue.write_action_tools,
|
||||
tools: out
|
||||
.catalogue
|
||||
.tools
|
||||
.into_iter()
|
||||
.map(|tool| McpToolCatalogueEntryDto {
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
access: match tool.access {
|
||||
application::McpToolCatalogueAccess::Read => "read",
|
||||
application::McpToolCatalogueAccess::WriteAction => "writeAction",
|
||||
}
|
||||
.to_owned(),
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
project_default: out.permissions.project_default,
|
||||
agents: out.permissions.agents,
|
||||
@ -3068,6 +3096,20 @@ pub struct UpdateAgentMcpToolPermissionsRequestDto {
|
||||
pub policy: Option<McpToolPolicy>,
|
||||
}
|
||||
|
||||
/// Request for atomically toggling one native MCP tool for an agent.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SetAgentMcpToolAllowedRequestDto {
|
||||
/// Id of the owning project.
|
||||
pub project_id: String,
|
||||
/// Target agent id.
|
||||
pub agent_id: String,
|
||||
/// Exact native MCP tool name.
|
||||
pub tool_name: String,
|
||||
/// Desired explicit allowlist membership.
|
||||
pub allowed: bool,
|
||||
}
|
||||
|
||||
/// Request DTO for `update_project_context`.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -5782,7 +5824,11 @@ mod tests {
|
||||
vec!["idea_ticket_read".to_owned()],
|
||||
vec!["idea_ticket_update".to_owned()],
|
||||
)
|
||||
.unwrap(),
|
||||
.unwrap()
|
||||
.with_descriptions([
|
||||
("idea_ticket_read", "Read one ticket."),
|
||||
("idea_ticket_update", "Update one ticket."),
|
||||
]),
|
||||
permissions: ProjectMcpToolPermissions {
|
||||
version: 1,
|
||||
project_default: Some(
|
||||
@ -5804,7 +5850,19 @@ mod tests {
|
||||
"version": 1,
|
||||
"catalogue": {
|
||||
"readOnlyTools": ["idea_ticket_read"],
|
||||
"writeActionTools": ["idea_ticket_update"]
|
||||
"writeActionTools": ["idea_ticket_update"],
|
||||
"tools": [
|
||||
{
|
||||
"name": "idea_ticket_read",
|
||||
"description": "Read one ticket.",
|
||||
"access": "read"
|
||||
},
|
||||
{
|
||||
"name": "idea_ticket_update",
|
||||
"description": "Update one ticket.",
|
||||
"access": "writeAction"
|
||||
}
|
||||
]
|
||||
},
|
||||
"projectDefault": {
|
||||
"allowedTools": ["idea_ticket_read"]
|
||||
|
||||
@ -52,16 +52,17 @@ use application::{
|
||||
ResolveManagedTemp, ResolveMemoryLinks, RestoreOpenWindows, RetryBackgroundTask,
|
||||
ReviewPluginPackage, RevokeAllDevices, RevokeDevice, RotateConversationLog, SaveChatDraft,
|
||||
SaveEmbedderProfile, SaveModelServer, SaveOpenCodeProviderProfile, SaveProfile,
|
||||
SessionLimitService, SetActiveLayout, SetPluginEnabled, SetProjectPluginEnabled,
|
||||
SlashCommandRegistry, SnapshotOpenWindows, SnapshotRunningAgents, SpawnBackgroundCommand,
|
||||
StopLiveAgent, StructuredRoutingMode, StructuredSessions, SuggestedThisSession,
|
||||
SyncAgentWithTemplate, TerminalSessions, TouchDevice, UnassignSkillFromAgent,
|
||||
UnassignTicketFromSprint, UninstallPlugin, UnlinkIssues, UpdateAgentContactable,
|
||||
UpdateAgentContext, UpdateAgentEffort, UpdateAgentMcpToolPermissions, UpdateAgentPermissions,
|
||||
UpdateAgentSystemPermissions, UpdateIssue, UpdateIssueCarnet, UpdateLiveState, UpdateMemory,
|
||||
UpdateProjectContext, UpdateProjectMcpToolPermissions, UpdateProjectOrchestrator,
|
||||
UpdateProjectPermissions, UpdateProjectSystemPermissions, UpdateSkill, UpdateTemplate,
|
||||
WakeSessionProvider, WriteMemory, WriteToTerminal, AGENT_MEMORY_RECALL_BUDGET,
|
||||
SessionLimitService, SetActiveLayout, SetAgentMcpToolAllowed, SetPluginEnabled,
|
||||
SetProjectPluginEnabled, SlashCommandRegistry, SnapshotOpenWindows, SnapshotRunningAgents,
|
||||
SpawnBackgroundCommand, StopLiveAgent, StructuredRoutingMode, StructuredSessions,
|
||||
SuggestedThisSession, SyncAgentWithTemplate, TerminalSessions, TouchDevice,
|
||||
UnassignSkillFromAgent, UnassignTicketFromSprint, UninstallPlugin, UnlinkIssues,
|
||||
UpdateAgentContactable, UpdateAgentContext, UpdateAgentEffort, UpdateAgentMcpToolPermissions,
|
||||
UpdateAgentPermissions, UpdateAgentSystemPermissions, UpdateIssue, UpdateIssueCarnet,
|
||||
UpdateLiveState, UpdateMemory, UpdateProjectContext, UpdateProjectMcpToolPermissions,
|
||||
UpdateProjectOrchestrator, UpdateProjectPermissions, UpdateProjectSystemPermissions,
|
||||
UpdateSkill, UpdateTemplate, WakeSessionProvider, WriteMemory, WriteToTerminal,
|
||||
AGENT_MEMORY_RECALL_BUDGET,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use domain::events::AgentRestartPhase;
|
||||
@ -291,6 +292,32 @@ mod backend_core_build_tests {
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn backend_core_exposes_structured_native_mcp_catalogue_with_descriptions() {
|
||||
let dir = std::env::temp_dir().join(format!("idea-mcp-catalogue-{}", uuid::Uuid::new_v4()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let core = BackendCore::build(dir.clone());
|
||||
let output = core
|
||||
.read_mcp_tool_permissions
|
||||
.execute(application::ReadMcpToolPermissionsInput {
|
||||
project: project(&dir),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
output.catalogue.tools.len(),
|
||||
output.catalogue.read_only_tools.len() + output.catalogue.write_action_tools.len()
|
||||
);
|
||||
assert!(output
|
||||
.catalogue
|
||||
.tools
|
||||
.iter()
|
||||
.all(|tool| !tool.name.is_empty() && !tool.description.is_empty()));
|
||||
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_directory_resolution_accepts_relative_directory_under_project_root() {
|
||||
let root = std::env::temp_dir().join(format!("idea-plugin-root-{}", Uuid::new_v4()));
|
||||
@ -1939,6 +1966,8 @@ pub struct BackendCore {
|
||||
pub update_project_mcp_tool_permissions: Arc<UpdateProjectMcpToolPermissions>,
|
||||
/// Replace or clear one agent MCP tool policy override.
|
||||
pub update_agent_mcp_tool_permissions: Arc<UpdateAgentMcpToolPermissions>,
|
||||
/// Atomically toggle one tool in an agent's explicit native MCP allowlist.
|
||||
pub set_agent_mcp_tool_allowed: Arc<SetAgentMcpToolAllowed>,
|
||||
/// Registre des sessions structurées (IA / cellules chat, §17.5). Partagé avec
|
||||
/// `LaunchAgent`/`ChangeAgentProfile` ; consommé par les commandes de chat (D4)
|
||||
/// pour résoudre la session vivante d'un `sessionId` et l'arrêter à la fermeture.
|
||||
@ -2858,7 +2887,12 @@ impl BackendCore {
|
||||
.map(|tool| (*tool).to_owned())
|
||||
.collect(),
|
||||
)
|
||||
.expect("MCP tool catalogue classification is valid");
|
||||
.expect("MCP tool catalogue classification is valid")
|
||||
.with_descriptions(
|
||||
infrastructure::orchestrator::mcp::tools::catalogue()
|
||||
.into_iter()
|
||||
.map(|tool| (tool.name, tool.description)),
|
||||
);
|
||||
let read_mcp_tool_permissions = Arc::new(ReadMcpToolPermissions::new(
|
||||
Arc::clone(&mcp_tool_permission_store_port),
|
||||
mcp_tool_catalogue.clone(),
|
||||
@ -2868,6 +2902,10 @@ impl BackendCore {
|
||||
mcp_tool_catalogue.clone(),
|
||||
));
|
||||
let update_agent_mcp_tool_permissions = Arc::new(UpdateAgentMcpToolPermissions::new(
|
||||
Arc::clone(&mcp_tool_permission_store_port),
|
||||
mcp_tool_catalogue.clone(),
|
||||
));
|
||||
let set_agent_mcp_tool_allowed = Arc::new(SetAgentMcpToolAllowed::new(
|
||||
Arc::clone(&mcp_tool_permission_store_port),
|
||||
mcp_tool_catalogue,
|
||||
));
|
||||
@ -4061,6 +4099,7 @@ impl BackendCore {
|
||||
read_mcp_tool_permissions,
|
||||
update_project_mcp_tool_permissions,
|
||||
update_agent_mcp_tool_permissions,
|
||||
set_agent_mcp_tool_allowed,
|
||||
structured_sessions,
|
||||
create_agent,
|
||||
list_agents,
|
||||
|
||||
Reference in New Issue
Block a user