feat(backend): API Tauri pour la gestion des permissions tools MCP (#82 lot B4)
Expose au niveau application/DTO/commandes Tauri le catalogue et les permissions des tools MCP (application/mcp_tool_permissions.rs, dto.rs, commands.rs) pour une future UI de gestion. Lot B4 du ticket #82, dernier lot backend : ferme la boucle sur B1 (domaine/store) + B2 (enforcement MCP stdio) + B3 (parité OpenAI-compatible). QA vert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@ -1479,9 +1479,12 @@ pub fn parse_profile_id(raw: &str) -> Result<ProfileId, ErrorDto> {
|
||||
|
||||
use application::{
|
||||
ChangeAgentProfileOutput, CreateAgentOutput, InspectConversationOutput, LaunchAgentOutput,
|
||||
ListAgentsOutput, ReadAgentContextOutput,
|
||||
ListAgentsOutput, ReadAgentContextOutput, ReadMcpToolPermissionsOutput,
|
||||
};
|
||||
use domain::{
|
||||
Agent, AgentMcpToolPolicyOverride, EffectivePermissions, McpToolPolicy, PermissionSet,
|
||||
ProjectPermissions, TerminalSession,
|
||||
};
|
||||
use domain::{Agent, EffectivePermissions, PermissionSet, ProjectPermissions, TerminalSession};
|
||||
|
||||
/// An agent crossing the wire. [`Agent`] already serialises camelCase
|
||||
/// (`id`, `name`, `contextPath`, `profileId`, `origin` tagged, `synchronized`),
|
||||
@ -1564,6 +1567,44 @@ pub struct ProjectPermissionsDto(pub ProjectPermissions);
|
||||
#[serde(transparent)]
|
||||
pub struct EffectivePermissionsDto(pub EffectivePermissions);
|
||||
|
||||
/// Canonical MCP tool catalogue classification crossing the wire.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct McpToolCatalogueDto {
|
||||
/// Tools allowed by the default read-only fallback.
|
||||
pub read_only_tools: Vec<String>,
|
||||
/// Tools treated as writing/action/execution tools.
|
||||
pub write_action_tools: Vec<String>,
|
||||
}
|
||||
|
||||
/// Full MCP tool permission state crossing the wire.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProjectMcpToolPermissionsDto {
|
||||
/// Document format version.
|
||||
pub version: u32,
|
||||
/// Canonical catalogue classification used for validation and display.
|
||||
pub catalogue: McpToolCatalogueDto,
|
||||
/// Optional project-wide default MCP tool policy.
|
||||
pub project_default: Option<McpToolPolicy>,
|
||||
/// Per-agent overrides.
|
||||
pub agents: Vec<AgentMcpToolPolicyOverride>,
|
||||
}
|
||||
|
||||
impl From<ReadMcpToolPermissionsOutput> for ProjectMcpToolPermissionsDto {
|
||||
fn from(out: ReadMcpToolPermissionsOutput) -> Self {
|
||||
Self {
|
||||
version: out.permissions.version,
|
||||
catalogue: McpToolCatalogueDto {
|
||||
read_only_tools: out.catalogue.read_only_tools,
|
||||
write_action_tools: out.catalogue.write_action_tools,
|
||||
},
|
||||
project_default: out.permissions.project_default,
|
||||
agents: out.permissions.agents,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Request DTO for updating project default permissions.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -1596,6 +1637,28 @@ pub struct ResolveAgentPermissionsRequestDto {
|
||||
pub agent_id: String,
|
||||
}
|
||||
|
||||
/// Request DTO for updating project default MCP tool permissions.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateProjectMcpToolPermissionsRequestDto {
|
||||
/// Id of the owning project.
|
||||
pub project_id: String,
|
||||
/// New project MCP tool policy. `null` removes the default.
|
||||
pub policy: Option<McpToolPolicy>,
|
||||
}
|
||||
|
||||
/// Request DTO for updating one agent MCP tool permission override.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateAgentMcpToolPermissionsRequestDto {
|
||||
/// Id of the owning project.
|
||||
pub project_id: String,
|
||||
/// Target agent id.
|
||||
pub agent_id: String,
|
||||
/// New agent MCP tool policy. `null` removes the override.
|
||||
pub policy: Option<McpToolPolicy>,
|
||||
}
|
||||
|
||||
/// Request DTO for `update_project_context`.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -3493,3 +3556,59 @@ pub struct SpawnBackgroundCommandRequestDto {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub deadline_ms: Option<u64>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use application::McpToolPermissionCatalogue;
|
||||
use domain::{AgentId, ProjectMcpToolPermissions};
|
||||
use serde_json::json;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn project_mcp_tool_permissions_dto_uses_stable_camel_case_contract() {
|
||||
let known_tools = ["idea_ticket_read", "idea_ticket_update"];
|
||||
let agent_id = AgentId::from_uuid(Uuid::from_u128(42));
|
||||
let output = ReadMcpToolPermissionsOutput {
|
||||
catalogue: McpToolPermissionCatalogue::new(
|
||||
vec!["idea_ticket_read".to_owned()],
|
||||
vec!["idea_ticket_update".to_owned()],
|
||||
)
|
||||
.unwrap(),
|
||||
permissions: ProjectMcpToolPermissions {
|
||||
version: 1,
|
||||
project_default: Some(
|
||||
McpToolPolicy::new(vec!["idea_ticket_read".to_owned()], &known_tools).unwrap(),
|
||||
),
|
||||
agents: vec![AgentMcpToolPolicyOverride::new(
|
||||
agent_id,
|
||||
McpToolPolicy::new(vec!["idea_ticket_update".to_owned()], &known_tools)
|
||||
.unwrap(),
|
||||
)],
|
||||
},
|
||||
};
|
||||
|
||||
let value = serde_json::to_value(ProjectMcpToolPermissionsDto::from(output)).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
value,
|
||||
json!({
|
||||
"version": 1,
|
||||
"catalogue": {
|
||||
"readOnlyTools": ["idea_ticket_read"],
|
||||
"writeActionTools": ["idea_ticket_update"]
|
||||
},
|
||||
"projectDefault": {
|
||||
"allowedTools": ["idea_ticket_read"]
|
||||
},
|
||||
"agents": [{
|
||||
"agentId": agent_id,
|
||||
"policy": {
|
||||
"allowedTools": ["idea_ticket_update"]
|
||||
}
|
||||
}]
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,20 +28,21 @@ use application::{
|
||||
ListIssues, ListLayouts, ListMemories, ListModelServers, ListProfiles, ListProjects,
|
||||
ListResumableAgents, ListSkills, ListSprints, ListTemplates, LiveAgentRegistry, LiveSessions,
|
||||
LiveStateLeanProvider, LiveStateProvider, LiveStateReadProvider, LoadLayout, McpRuntime,
|
||||
MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject, OpenTerminal,
|
||||
OpenTicketAssistant, OrchestratorService, PairAttemptLimiter, PairDevice,
|
||||
McpToolPermissionCatalogue, MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject,
|
||||
OpenTerminal, OpenTicketAssistant, OrchestratorService, PairAttemptLimiter, PairDevice,
|
||||
PermissionProjectorRegistry, ProposeContext, ReadAgentContext, ReadContext,
|
||||
ReadConversationPage, ReadIssue, ReadIssueCarnet, ReadMemory, ReadMemoryIndex,
|
||||
ReadProjectContext, ReadSkill, RecallMemory, ReconcileLayouts, ReconcileLiveState,
|
||||
ReconcileLiveStateInput, RecordTurn, RecordTurnProvider, ReferenceProfiles, RenameDevice,
|
||||
RenameLayout, RenameSprint, ReorderSprints, ResizeTerminal, ResolveAgentPermissions,
|
||||
ResolveMemoryLinks, RestoreOpenWindows, RetryBackgroundTask, RevokeAllDevices, RevokeDevice,
|
||||
RotateConversationLog, SaveEmbedderProfile, SaveModelServer, SaveProfile, SessionLimitService,
|
||||
SetActiveLayout, SnapshotOpenWindows, SnapshotRunningAgents, SpawnBackgroundCommand,
|
||||
StopLiveAgent, StructuredRoutingMode, StructuredSessions, SuggestedThisSession,
|
||||
SyncAgentWithTemplate, TerminalSessions, TouchDevice, UnassignSkillFromAgent,
|
||||
UnassignTicketFromSprint, UnlinkIssues, UpdateAgentContext, UpdateAgentPermissions,
|
||||
UpdateIssue, UpdateIssueCarnet, UpdateLiveState, UpdateMemory, UpdateProjectContext,
|
||||
ReadConversationPage, ReadIssue, ReadIssueCarnet, ReadMcpToolPermissions, ReadMemory,
|
||||
ReadMemoryIndex, ReadProjectContext, ReadSkill, RecallMemory, ReconcileLayouts,
|
||||
ReconcileLiveState, ReconcileLiveStateInput, RecordTurn, RecordTurnProvider, ReferenceProfiles,
|
||||
RenameDevice, RenameLayout, RenameSprint, ReorderSprints, ResizeTerminal,
|
||||
ResolveAgentPermissions, ResolveMemoryLinks, RestoreOpenWindows, RetryBackgroundTask,
|
||||
RevokeAllDevices, RevokeDevice, RotateConversationLog, SaveEmbedderProfile, SaveModelServer,
|
||||
SaveProfile, SessionLimitService, SetActiveLayout, SnapshotOpenWindows, SnapshotRunningAgents,
|
||||
SpawnBackgroundCommand, StopLiveAgent, StructuredRoutingMode, StructuredSessions,
|
||||
SuggestedThisSession, SyncAgentWithTemplate, TerminalSessions, TouchDevice,
|
||||
UnassignSkillFromAgent, UnassignTicketFromSprint, UnlinkIssues, UpdateAgentContext,
|
||||
UpdateAgentMcpToolPermissions, UpdateAgentPermissions, UpdateIssue, UpdateIssueCarnet,
|
||||
UpdateLiveState, UpdateMemory, UpdateProjectContext, UpdateProjectMcpToolPermissions,
|
||||
UpdateProjectPermissions, UpdateSkill, UpdateTemplate, WakeSessionProvider, WriteMemory,
|
||||
WriteToTerminal, AGENT_MEMORY_RECALL_BUDGET,
|
||||
};
|
||||
@ -944,6 +945,12 @@ pub struct BackendCore {
|
||||
pub tool_policy_registry: Arc<ToolPolicyRegistry>,
|
||||
/// Durable per-agent MCP tool permission store.
|
||||
pub mcp_tool_permission_store: Arc<dyn McpToolPermissionStore>,
|
||||
/// Read durable MCP tool permissions plus canonical catalogue classification.
|
||||
pub read_mcp_tool_permissions: Arc<ReadMcpToolPermissions>,
|
||||
/// Replace or clear the project-wide MCP tool policy.
|
||||
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>,
|
||||
/// 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.
|
||||
@ -1511,6 +1518,29 @@ impl BackendCore {
|
||||
Arc::new(FsMcpToolPermissionStore::new(Arc::clone(&fs_port)));
|
||||
let mcp_tool_permission_store_port =
|
||||
Arc::clone(&mcp_tool_permission_store) as Arc<dyn McpToolPermissionStore>;
|
||||
let mcp_tool_catalogue = McpToolPermissionCatalogue::new(
|
||||
infrastructure::orchestrator::mcp::tools::READ_ONLY_TOOLS
|
||||
.iter()
|
||||
.map(|tool| (*tool).to_owned())
|
||||
.collect(),
|
||||
infrastructure::orchestrator::mcp::tools::WRITE_ACTION_TOOLS
|
||||
.iter()
|
||||
.map(|tool| (*tool).to_owned())
|
||||
.collect(),
|
||||
)
|
||||
.expect("MCP tool catalogue classification is valid");
|
||||
let read_mcp_tool_permissions = Arc::new(ReadMcpToolPermissions::new(
|
||||
Arc::clone(&mcp_tool_permission_store_port),
|
||||
mcp_tool_catalogue.clone(),
|
||||
));
|
||||
let update_project_mcp_tool_permissions = Arc::new(UpdateProjectMcpToolPermissions::new(
|
||||
Arc::clone(&mcp_tool_permission_store_port),
|
||||
mcp_tool_catalogue.clone(),
|
||||
));
|
||||
let update_agent_mcp_tool_permissions = Arc::new(UpdateAgentMcpToolPermissions::new(
|
||||
Arc::clone(&mcp_tool_permission_store_port),
|
||||
mcp_tool_catalogue,
|
||||
));
|
||||
|
||||
// --- Skill store (L12) ---
|
||||
// Global skills live in the machine-local app-data dir; project skills are
|
||||
@ -2446,6 +2476,9 @@ impl BackendCore {
|
||||
close_ticket_assistant,
|
||||
tool_policy_registry,
|
||||
mcp_tool_permission_store: mcp_tool_permission_store_port,
|
||||
read_mcp_tool_permissions,
|
||||
update_project_mcp_tool_permissions,
|
||||
update_agent_mcp_tool_permissions,
|
||||
structured_sessions,
|
||||
create_agent,
|
||||
list_agents,
|
||||
|
||||
Reference in New Issue
Block a user