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:
2026-07-18 23:15:05 +02:00
parent eeba0ddb46
commit c84a66f4e8
6 changed files with 662 additions and 32 deletions

View File

@ -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"]
}
}]
})
);
}
}