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:
@ -19,12 +19,14 @@ use application::{
|
|||||||
LaunchAgentInput, ListAgentsInput, ListDevicesInput, ListLayoutsInput, ListMemoriesInput,
|
LaunchAgentInput, ListAgentsInput, ListDevicesInput, ListLayoutsInput, ListMemoriesInput,
|
||||||
ListResumableAgentsInput, ListSkillsInput, LiveSessions, LoadLayoutInput, McpRuntime,
|
ListResumableAgentsInput, ListSkillsInput, LiveSessions, LoadLayoutInput, McpRuntime,
|
||||||
MutateLayoutInput, OpenProjectInput, ReadAgentContextInput, ReadConversationPageInput,
|
MutateLayoutInput, OpenProjectInput, ReadAgentContextInput, ReadConversationPageInput,
|
||||||
ReadMemoryIndexInput, ReadProjectContextInput, RecallMemoryInput, ReconcileLayoutsInput,
|
ReadMcpToolPermissionsInput, ReadMemoryIndexInput, ReadProjectContextInput, RecallMemoryInput,
|
||||||
ReconcileLiveStateInput, RenameDeviceInput, RenameLayoutInput, ResolveAgentPermissionsInput,
|
ReconcileLayoutsInput, ReconcileLiveStateInput, RenameDeviceInput, RenameLayoutInput,
|
||||||
ResolveMemoryLinksInput, RevokeDeviceInput, RotateConversationLogInput, SetActiveLayoutInput,
|
ResolveAgentPermissionsInput, ResolveMemoryLinksInput, RevokeDeviceInput,
|
||||||
SnapshotRunningAgentsInput, StopLiveAgentInput, SyncAgentWithTemplateInput,
|
RotateConversationLogInput, SetActiveLayoutInput, SnapshotRunningAgentsInput,
|
||||||
UnassignSkillFromAgentInput, UpdateAgentContextInput, UpdateAgentPermissionsInput,
|
StopLiveAgentInput, SyncAgentWithTemplateInput, UnassignSkillFromAgentInput,
|
||||||
UpdateMemoryInput, UpdateProjectContextInput, UpdateProjectPermissionsInput, UpdateSkillInput,
|
UpdateAgentContextInput, UpdateAgentMcpToolPermissionsInput, UpdateAgentPermissionsInput,
|
||||||
|
UpdateMemoryInput, UpdateProjectContextInput, UpdateProjectMcpToolPermissionsInput,
|
||||||
|
UpdateProjectPermissionsInput, UpdateSkillInput,
|
||||||
};
|
};
|
||||||
use domain::ports::ModelServerRuntime;
|
use domain::ports::ModelServerRuntime;
|
||||||
use domain::ports::PtyHandle;
|
use domain::ports::PtyHandle;
|
||||||
@ -49,17 +51,19 @@ use crate::dto::{
|
|||||||
LiveAgentListDto, MemoryDto, MemoryIndexDto, MemoryLinksDto, MemoryListDto,
|
LiveAgentListDto, MemoryDto, MemoryIndexDto, MemoryLinksDto, MemoryListDto,
|
||||||
ModelServerConfigDto, ModelServerConfigListDto, OpenTerminalRequestDto,
|
ModelServerConfigDto, ModelServerConfigListDto, OpenTerminalRequestDto,
|
||||||
PreviewModelServerCommandDto, ProfileDto, ProfileListDto, ProjectDto, ProjectListDto,
|
PreviewModelServerCommandDto, ProfileDto, ProfileListDto, ProjectDto, ProjectListDto,
|
||||||
ProjectPermissionsDto, ProjectWorkStateDto, ReadAgentContextResponseDto,
|
ProjectMcpToolPermissionsDto, ProjectPermissionsDto, ProjectWorkStateDto,
|
||||||
ReadConversationPageRequestDto, ReattachChatDto, ReattachResultDto, RecallMemoryRequestDto,
|
ReadAgentContextResponseDto, ReadConversationPageRequestDto, ReattachChatDto,
|
||||||
RenameLayoutRequestDto, ReplyChunk, ResizeTerminalRequestDto,
|
ReattachResultDto, RecallMemoryRequestDto, RenameLayoutRequestDto, ReplyChunk,
|
||||||
ResolveAgentPermissionsRequestDto, ResumableAgentListDto, SaveEmbedderProfileRequestDto,
|
ResizeTerminalRequestDto, ResolveAgentPermissionsRequestDto, ResumableAgentListDto,
|
||||||
SaveModelServerRequestDto, SaveProfileRequestDto, SetActiveLayoutRequestDto,
|
SaveEmbedderProfileRequestDto, SaveModelServerRequestDto, SaveProfileRequestDto,
|
||||||
SetActiveLayoutResultDto, SkillDto, SkillListDto, StopLiveAgentRequestDto,
|
SetActiveLayoutRequestDto, SetActiveLayoutResultDto, SkillDto, SkillListDto,
|
||||||
StopLiveAgentResponseDto, SyncAgentWithTemplateRequestDto, SyncResultDto, TemplateDto,
|
StopLiveAgentRequestDto, StopLiveAgentResponseDto, SyncAgentWithTemplateRequestDto,
|
||||||
TemplateListDto, TerminalClosedDto, TerminalSessionDto, TurnPageDto, UnassignSkillRequestDto,
|
SyncResultDto, TemplateDto, TemplateListDto, TerminalClosedDto, TerminalSessionDto,
|
||||||
UpdateAgentContextRequestDto, UpdateAgentPermissionsRequestDto, UpdateMemoryRequestDto,
|
TurnPageDto, UnassignSkillRequestDto, UpdateAgentContextRequestDto,
|
||||||
UpdateProjectContextRequestDto, UpdateProjectPermissionsRequestDto, UpdateSkillRequestDto,
|
UpdateAgentMcpToolPermissionsRequestDto, UpdateAgentPermissionsRequestDto,
|
||||||
UpdateTemplateRequestDto, WriteTerminalRequestDto,
|
UpdateMemoryRequestDto, UpdateProjectContextRequestDto,
|
||||||
|
UpdateProjectMcpToolPermissionsRequestDto, UpdateProjectPermissionsRequestDto,
|
||||||
|
UpdateSkillRequestDto, UpdateTemplateRequestDto, WriteTerminalRequestDto,
|
||||||
};
|
};
|
||||||
use crate::embedded_server::{
|
use crate::embedded_server::{
|
||||||
EmbeddedServerStatusDto, ServerExposurePreviewDto, ServerExposureSettingsDto,
|
EmbeddedServerStatusDto, ServerExposurePreviewDto, ServerExposureSettingsDto,
|
||||||
@ -544,6 +548,68 @@ pub async fn resolve_agent_permissions(
|
|||||||
.map_err(ErrorDto::from)
|
.map_err(ErrorDto::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `get_mcp_tool_permissions` — read `.ideai/mcp-tool-permissions.json` plus catalogue.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an [`ErrorDto`] on invalid project id, invalid stored policy, or store failure.
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_mcp_tool_permissions(
|
||||||
|
project_id: String,
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
) -> Result<ProjectMcpToolPermissionsDto, ErrorDto> {
|
||||||
|
let project = resolve_project(&project_id, &state).await?;
|
||||||
|
state
|
||||||
|
.read_mcp_tool_permissions
|
||||||
|
.execute(ReadMcpToolPermissionsInput { project })
|
||||||
|
.await
|
||||||
|
.map(ProjectMcpToolPermissionsDto::from)
|
||||||
|
.map_err(ErrorDto::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `update_project_mcp_tool_permissions` — replace or remove project MCP tool defaults.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an [`ErrorDto`] on invalid project id, invalid tool policy, or store failure.
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn update_project_mcp_tool_permissions(
|
||||||
|
request: UpdateProjectMcpToolPermissionsRequestDto,
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
) -> Result<ProjectMcpToolPermissionsDto, ErrorDto> {
|
||||||
|
let project = resolve_project(&request.project_id, &state).await?;
|
||||||
|
state
|
||||||
|
.update_project_mcp_tool_permissions
|
||||||
|
.execute(UpdateProjectMcpToolPermissionsInput {
|
||||||
|
project,
|
||||||
|
policy: request.policy,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(ProjectMcpToolPermissionsDto::from)
|
||||||
|
.map_err(ErrorDto::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `update_agent_mcp_tool_permissions` — replace or remove one agent MCP tool override.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an [`ErrorDto`] on invalid ids, invalid tool policy, or store failure.
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn update_agent_mcp_tool_permissions(
|
||||||
|
request: UpdateAgentMcpToolPermissionsRequestDto,
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
) -> Result<ProjectMcpToolPermissionsDto, ErrorDto> {
|
||||||
|
let project = resolve_project(&request.project_id, &state).await?;
|
||||||
|
let agent_id = parse_agent_id(&request.agent_id)?;
|
||||||
|
state
|
||||||
|
.update_agent_mcp_tool_permissions
|
||||||
|
.execute(UpdateAgentMcpToolPermissionsInput {
|
||||||
|
project,
|
||||||
|
agent_id,
|
||||||
|
policy: request.policy,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(ProjectMcpToolPermissionsDto::from)
|
||||||
|
.map_err(ErrorDto::from)
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Terminals (L3)
|
// Terminals (L3)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@ -176,6 +176,9 @@ pub fn run() {
|
|||||||
commands::update_project_permissions,
|
commands::update_project_permissions,
|
||||||
commands::update_agent_permissions,
|
commands::update_agent_permissions,
|
||||||
commands::resolve_agent_permissions,
|
commands::resolve_agent_permissions,
|
||||||
|
commands::get_mcp_tool_permissions,
|
||||||
|
commands::update_project_mcp_tool_permissions,
|
||||||
|
commands::update_agent_mcp_tool_permissions,
|
||||||
commands::open_terminal,
|
commands::open_terminal,
|
||||||
commands::write_terminal,
|
commands::write_terminal,
|
||||||
commands::resize_terminal,
|
commands::resize_terminal,
|
||||||
|
|||||||
@ -22,6 +22,7 @@ pub mod git;
|
|||||||
pub mod health;
|
pub mod health;
|
||||||
pub mod issues;
|
pub mod issues;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
pub mod mcp_tool_permissions;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod model_server;
|
pub mod model_server;
|
||||||
pub mod orchestrator;
|
pub mod orchestrator;
|
||||||
@ -105,6 +106,12 @@ pub use layout::{
|
|||||||
SetActiveLayout, SetActiveLayoutInput, SetActiveLayoutOutput, SnapshotRunningAgents,
|
SetActiveLayout, SetActiveLayoutInput, SetActiveLayoutOutput, SnapshotRunningAgents,
|
||||||
SnapshotRunningAgentsInput, SnapshotRunningAgentsOutput, LAYOUTS_FILE,
|
SnapshotRunningAgentsInput, SnapshotRunningAgentsOutput, LAYOUTS_FILE,
|
||||||
};
|
};
|
||||||
|
pub use mcp_tool_permissions::{
|
||||||
|
McpToolPermissionCatalogue, ReadMcpToolPermissions, ReadMcpToolPermissionsInput,
|
||||||
|
ReadMcpToolPermissionsOutput, UpdateAgentMcpToolPermissions,
|
||||||
|
UpdateAgentMcpToolPermissionsInput, UpdateProjectMcpToolPermissions,
|
||||||
|
UpdateProjectMcpToolPermissionsInput,
|
||||||
|
};
|
||||||
pub use memory::{
|
pub use memory::{
|
||||||
CreateMemory, CreateMemoryInput, CreateMemoryOutput, DeleteMemory, DeleteMemoryInput,
|
CreateMemory, CreateMemoryInput, CreateMemoryOutput, DeleteMemory, DeleteMemoryInput,
|
||||||
GetMemory, GetMemoryInput, GetMemoryOutput, HarvestMemoryFromTurn, ListMemories,
|
GetMemory, GetMemoryInput, GetMemoryOutput, HarvestMemoryFromTurn, ListMemories,
|
||||||
|
|||||||
402
crates/application/src/mcp_tool_permissions.rs
Normal file
402
crates/application/src/mcp_tool_permissions.rs
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
//! MCP tool permission use cases.
|
||||||
|
//!
|
||||||
|
//! These use cases expose the durable per-project MCP tool policy document while
|
||||||
|
//! keeping the application layer independent from the concrete MCP catalogue
|
||||||
|
//! adapter. The catalogue classification is injected by the composition root.
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use domain::ports::McpToolPermissionStore;
|
||||||
|
use domain::{
|
||||||
|
AgentId, AgentMcpToolPolicyOverride, McpToolPermissionError, McpToolPolicy, Project,
|
||||||
|
ProjectMcpToolPermissions,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::error::AppError;
|
||||||
|
|
||||||
|
/// Read/write MCP tool catalogue classification used to validate policies.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct McpToolPermissionCatalogue {
|
||||||
|
/// Tools allowed by the default fallback policy.
|
||||||
|
pub read_only_tools: Vec<String>,
|
||||||
|
/// Tools denied by default because they write, act, or execute.
|
||||||
|
pub write_action_tools: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McpToolPermissionCatalogue {
|
||||||
|
/// Builds a catalogue from read-only and write/action tool names.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`AppError::Invalid`] if a tool name is empty or appears twice.
|
||||||
|
pub fn new(
|
||||||
|
read_only_tools: Vec<String>,
|
||||||
|
write_action_tools: Vec<String>,
|
||||||
|
) -> Result<Self, AppError> {
|
||||||
|
let catalogue = Self {
|
||||||
|
read_only_tools,
|
||||||
|
write_action_tools,
|
||||||
|
};
|
||||||
|
catalogue.validate()?;
|
||||||
|
Ok(catalogue)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all known tool names as borrowed strings.
|
||||||
|
#[must_use]
|
||||||
|
pub fn known_tool_refs(&self) -> Vec<&str> {
|
||||||
|
self.read_only_tools
|
||||||
|
.iter()
|
||||||
|
.chain(self.write_action_tools.iter())
|
||||||
|
.map(String::as_str)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate(&self) -> Result<(), AppError> {
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
for tool in self
|
||||||
|
.read_only_tools
|
||||||
|
.iter()
|
||||||
|
.chain(self.write_action_tools.iter())
|
||||||
|
{
|
||||||
|
if tool.is_empty() {
|
||||||
|
return Err(AppError::Invalid(
|
||||||
|
"MCP tool name cannot be empty".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if !seen.insert(tool.as_str()) {
|
||||||
|
return Err(AppError::Invalid(format!("duplicate MCP tool: {tool}")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads durable MCP tool permissions for a project.
|
||||||
|
pub struct ReadMcpToolPermissions {
|
||||||
|
store: Arc<dyn McpToolPermissionStore>,
|
||||||
|
catalogue: McpToolPermissionCatalogue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadMcpToolPermissions {
|
||||||
|
/// Builds the use case.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
store: Arc<dyn McpToolPermissionStore>,
|
||||||
|
catalogue: McpToolPermissionCatalogue,
|
||||||
|
) -> Self {
|
||||||
|
Self { store, catalogue }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes the read.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`AppError`] on store or validation failure.
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
input: ReadMcpToolPermissionsInput,
|
||||||
|
) -> Result<ReadMcpToolPermissionsOutput, AppError> {
|
||||||
|
let permissions = self.store.load_mcp_tool_permissions(&input.project).await?;
|
||||||
|
validate_doc(&permissions, &self.catalogue)?;
|
||||||
|
Ok(ReadMcpToolPermissionsOutput {
|
||||||
|
catalogue: self.catalogue.clone(),
|
||||||
|
permissions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Input for [`ReadMcpToolPermissions`].
|
||||||
|
pub struct ReadMcpToolPermissionsInput {
|
||||||
|
/// Target project.
|
||||||
|
pub project: Project,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Output for MCP tool permission reads and writes.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ReadMcpToolPermissionsOutput {
|
||||||
|
/// Canonical catalogue classification.
|
||||||
|
pub catalogue: McpToolPermissionCatalogue,
|
||||||
|
/// Persisted permission document.
|
||||||
|
pub permissions: ProjectMcpToolPermissions,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces the project-wide default MCP tool policy.
|
||||||
|
pub struct UpdateProjectMcpToolPermissions {
|
||||||
|
store: Arc<dyn McpToolPermissionStore>,
|
||||||
|
catalogue: McpToolPermissionCatalogue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpdateProjectMcpToolPermissions {
|
||||||
|
/// Builds the use case.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
store: Arc<dyn McpToolPermissionStore>,
|
||||||
|
catalogue: McpToolPermissionCatalogue,
|
||||||
|
) -> Self {
|
||||||
|
Self { store, catalogue }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes the mutation.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`AppError`] on store or validation failure.
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
input: UpdateProjectMcpToolPermissionsInput,
|
||||||
|
) -> Result<ReadMcpToolPermissionsOutput, AppError> {
|
||||||
|
validate_policy(input.policy.as_ref(), &self.catalogue)?;
|
||||||
|
let mut doc = self.store.load_mcp_tool_permissions(&input.project).await?;
|
||||||
|
doc.project_default = input.policy;
|
||||||
|
validate_doc(&doc, &self.catalogue)?;
|
||||||
|
self.store
|
||||||
|
.save_mcp_tool_permissions(&input.project, &doc)
|
||||||
|
.await?;
|
||||||
|
Ok(ReadMcpToolPermissionsOutput {
|
||||||
|
catalogue: self.catalogue.clone(),
|
||||||
|
permissions: doc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Input for [`UpdateProjectMcpToolPermissions`].
|
||||||
|
pub struct UpdateProjectMcpToolPermissionsInput {
|
||||||
|
/// Target project.
|
||||||
|
pub project: Project,
|
||||||
|
/// New project default policy. `None` removes the project default.
|
||||||
|
pub policy: Option<McpToolPolicy>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces or removes one agent MCP tool policy override.
|
||||||
|
pub struct UpdateAgentMcpToolPermissions {
|
||||||
|
store: Arc<dyn McpToolPermissionStore>,
|
||||||
|
catalogue: McpToolPermissionCatalogue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpdateAgentMcpToolPermissions {
|
||||||
|
/// Builds the use case.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
store: Arc<dyn McpToolPermissionStore>,
|
||||||
|
catalogue: McpToolPermissionCatalogue,
|
||||||
|
) -> Self {
|
||||||
|
Self { store, catalogue }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes the mutation.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// [`AppError`] on store or validation failure.
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
input: UpdateAgentMcpToolPermissionsInput,
|
||||||
|
) -> Result<ReadMcpToolPermissionsOutput, AppError> {
|
||||||
|
validate_policy(input.policy.as_ref(), &self.catalogue)?;
|
||||||
|
let mut doc = self.store.load_mcp_tool_permissions(&input.project).await?;
|
||||||
|
doc.agents
|
||||||
|
.retain(|override_| override_.agent_id != input.agent_id);
|
||||||
|
if let Some(policy) = input.policy {
|
||||||
|
doc.agents
|
||||||
|
.push(AgentMcpToolPolicyOverride::new(input.agent_id, policy));
|
||||||
|
}
|
||||||
|
validate_doc(&doc, &self.catalogue)?;
|
||||||
|
self.store
|
||||||
|
.save_mcp_tool_permissions(&input.project, &doc)
|
||||||
|
.await?;
|
||||||
|
Ok(ReadMcpToolPermissionsOutput {
|
||||||
|
catalogue: self.catalogue.clone(),
|
||||||
|
permissions: doc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Input for [`UpdateAgentMcpToolPermissions`].
|
||||||
|
pub struct UpdateAgentMcpToolPermissionsInput {
|
||||||
|
/// Target project.
|
||||||
|
pub project: Project,
|
||||||
|
/// Target agent.
|
||||||
|
pub agent_id: AgentId,
|
||||||
|
/// New override policy. `None` removes the override.
|
||||||
|
pub policy: Option<McpToolPolicy>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_policy(
|
||||||
|
policy: Option<&McpToolPolicy>,
|
||||||
|
catalogue: &McpToolPermissionCatalogue,
|
||||||
|
) -> Result<(), AppError> {
|
||||||
|
if let Some(policy) = policy {
|
||||||
|
policy
|
||||||
|
.validate(&catalogue.known_tool_refs())
|
||||||
|
.map_err(permission_error)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_doc(
|
||||||
|
doc: &ProjectMcpToolPermissions,
|
||||||
|
catalogue: &McpToolPermissionCatalogue,
|
||||||
|
) -> Result<(), AppError> {
|
||||||
|
doc.validate(&catalogue.known_tool_refs())
|
||||||
|
.map_err(permission_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn permission_error(error: McpToolPermissionError) -> AppError {
|
||||||
|
AppError::Invalid(error.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use domain::ids::ProjectId;
|
||||||
|
use domain::ports::StoreError;
|
||||||
|
use domain::project::ProjectPath;
|
||||||
|
use domain::remote::RemoteRef;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct FakeStore {
|
||||||
|
doc: Mutex<ProjectMcpToolPermissions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FakeStore {
|
||||||
|
fn new(doc: ProjectMcpToolPermissions) -> Self {
|
||||||
|
Self {
|
||||||
|
doc: Mutex::new(doc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl McpToolPermissionStore for FakeStore {
|
||||||
|
async fn load_mcp_tool_permissions(
|
||||||
|
&self,
|
||||||
|
_project: &Project,
|
||||||
|
) -> Result<ProjectMcpToolPermissions, StoreError> {
|
||||||
|
Ok(self.doc.lock().unwrap().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn save_mcp_tool_permissions(
|
||||||
|
&self,
|
||||||
|
_project: &Project,
|
||||||
|
permissions: &ProjectMcpToolPermissions,
|
||||||
|
) -> Result<(), StoreError> {
|
||||||
|
*self.doc.lock().unwrap() = permissions.clone();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn catalogue() -> McpToolPermissionCatalogue {
|
||||||
|
McpToolPermissionCatalogue::new(
|
||||||
|
vec!["idea_memory_read".to_owned(), "idea_ticket_list".to_owned()],
|
||||||
|
vec!["idea_memory_write".to_owned(), "idea_ask_agent".to_owned()],
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn project() -> Project {
|
||||||
|
Project::new(
|
||||||
|
ProjectId::from_uuid(Uuid::from_u128(1)),
|
||||||
|
"demo",
|
||||||
|
ProjectPath::new("/tmp/project").unwrap(),
|
||||||
|
RemoteRef::local(),
|
||||||
|
1_000,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn agent(n: u128) -> AgentId {
|
||||||
|
AgentId::from_uuid(Uuid::from_u128(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store(doc: ProjectMcpToolPermissions) -> Arc<dyn McpToolPermissionStore> {
|
||||||
|
Arc::new(FakeStore::new(doc))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn read_returns_classified_catalogue_and_current_document() {
|
||||||
|
let policy = McpToolPolicy::new(
|
||||||
|
vec!["idea_memory_write".to_owned()],
|
||||||
|
&catalogue().known_tool_refs(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let doc = ProjectMcpToolPermissions::new(
|
||||||
|
Some(policy),
|
||||||
|
Vec::new(),
|
||||||
|
&catalogue().known_tool_refs(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let use_case = ReadMcpToolPermissions::new(store(doc.clone()), catalogue());
|
||||||
|
|
||||||
|
let output = use_case
|
||||||
|
.execute(ReadMcpToolPermissionsInput { project: project() })
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(output.permissions, doc);
|
||||||
|
assert_eq!(
|
||||||
|
output.catalogue.read_only_tools,
|
||||||
|
vec!["idea_memory_read", "idea_ticket_list"]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
output.catalogue.write_action_tools,
|
||||||
|
vec!["idea_memory_write", "idea_ask_agent"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_rejects_unknown_tool() {
|
||||||
|
let use_case = UpdateAgentMcpToolPermissions::new(
|
||||||
|
store(ProjectMcpToolPermissions::default()),
|
||||||
|
catalogue(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let err = use_case
|
||||||
|
.execute(UpdateAgentMcpToolPermissionsInput {
|
||||||
|
project: project(),
|
||||||
|
agent_id: agent(7),
|
||||||
|
policy: Some(McpToolPolicy {
|
||||||
|
allowed_tools: vec!["idea_unknown".to_owned()],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap_err();
|
||||||
|
|
||||||
|
assert!(matches!(err, AppError::Invalid(message) if message.contains("idea_unknown")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_agent_override_roundtrips_through_read() {
|
||||||
|
let shared = store(ProjectMcpToolPermissions::default());
|
||||||
|
let update = UpdateAgentMcpToolPermissions::new(Arc::clone(&shared), catalogue());
|
||||||
|
let read = ReadMcpToolPermissions::new(Arc::clone(&shared), catalogue());
|
||||||
|
let agent_id = agent(9);
|
||||||
|
|
||||||
|
update
|
||||||
|
.execute(UpdateAgentMcpToolPermissionsInput {
|
||||||
|
project: project(),
|
||||||
|
agent_id,
|
||||||
|
policy: Some(
|
||||||
|
McpToolPolicy::new(
|
||||||
|
vec!["idea_memory_write".to_owned()],
|
||||||
|
&catalogue().known_tool_refs(),
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let output = read
|
||||||
|
.execute(ReadMcpToolPermissionsInput { project: project() })
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(output.permissions.agents.len(), 1);
|
||||||
|
assert_eq!(output.permissions.agents[0].agent_id, agent_id);
|
||||||
|
assert_eq!(
|
||||||
|
output.permissions.agents[0].policy.allowed_tools,
|
||||||
|
vec!["idea_memory_write"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1479,9 +1479,12 @@ pub fn parse_profile_id(raw: &str) -> Result<ProfileId, ErrorDto> {
|
|||||||
|
|
||||||
use application::{
|
use application::{
|
||||||
ChangeAgentProfileOutput, CreateAgentOutput, InspectConversationOutput, LaunchAgentOutput,
|
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
|
/// An agent crossing the wire. [`Agent`] already serialises camelCase
|
||||||
/// (`id`, `name`, `contextPath`, `profileId`, `origin` tagged, `synchronized`),
|
/// (`id`, `name`, `contextPath`, `profileId`, `origin` tagged, `synchronized`),
|
||||||
@ -1564,6 +1567,44 @@ pub struct ProjectPermissionsDto(pub ProjectPermissions);
|
|||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct EffectivePermissionsDto(pub EffectivePermissions);
|
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.
|
/// Request DTO for updating project default permissions.
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@ -1596,6 +1637,28 @@ pub struct ResolveAgentPermissionsRequestDto {
|
|||||||
pub agent_id: String,
|
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`.
|
/// Request DTO for `update_project_context`.
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@ -3493,3 +3556,59 @@ pub struct SpawnBackgroundCommandRequestDto {
|
|||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub deadline_ms: Option<u64>,
|
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,
|
ListIssues, ListLayouts, ListMemories, ListModelServers, ListProfiles, ListProjects,
|
||||||
ListResumableAgents, ListSkills, ListSprints, ListTemplates, LiveAgentRegistry, LiveSessions,
|
ListResumableAgents, ListSkills, ListSprints, ListTemplates, LiveAgentRegistry, LiveSessions,
|
||||||
LiveStateLeanProvider, LiveStateProvider, LiveStateReadProvider, LoadLayout, McpRuntime,
|
LiveStateLeanProvider, LiveStateProvider, LiveStateReadProvider, LoadLayout, McpRuntime,
|
||||||
MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject, OpenTerminal,
|
McpToolPermissionCatalogue, MoveTabToNewWindow, MutateLayout, OnnxModelView, OpenProject,
|
||||||
OpenTicketAssistant, OrchestratorService, PairAttemptLimiter, PairDevice,
|
OpenTerminal, OpenTicketAssistant, OrchestratorService, PairAttemptLimiter, PairDevice,
|
||||||
PermissionProjectorRegistry, ProposeContext, ReadAgentContext, ReadContext,
|
PermissionProjectorRegistry, ProposeContext, ReadAgentContext, ReadContext,
|
||||||
ReadConversationPage, ReadIssue, ReadIssueCarnet, ReadMemory, ReadMemoryIndex,
|
ReadConversationPage, ReadIssue, ReadIssueCarnet, ReadMcpToolPermissions, ReadMemory,
|
||||||
ReadProjectContext, ReadSkill, RecallMemory, ReconcileLayouts, ReconcileLiveState,
|
ReadMemoryIndex, ReadProjectContext, ReadSkill, RecallMemory, ReconcileLayouts,
|
||||||
ReconcileLiveStateInput, RecordTurn, RecordTurnProvider, ReferenceProfiles, RenameDevice,
|
ReconcileLiveState, ReconcileLiveStateInput, RecordTurn, RecordTurnProvider, ReferenceProfiles,
|
||||||
RenameLayout, RenameSprint, ReorderSprints, ResizeTerminal, ResolveAgentPermissions,
|
RenameDevice, RenameLayout, RenameSprint, ReorderSprints, ResizeTerminal,
|
||||||
ResolveMemoryLinks, RestoreOpenWindows, RetryBackgroundTask, RevokeAllDevices, RevokeDevice,
|
ResolveAgentPermissions, ResolveMemoryLinks, RestoreOpenWindows, RetryBackgroundTask,
|
||||||
RotateConversationLog, SaveEmbedderProfile, SaveModelServer, SaveProfile, SessionLimitService,
|
RevokeAllDevices, RevokeDevice, RotateConversationLog, SaveEmbedderProfile, SaveModelServer,
|
||||||
SetActiveLayout, SnapshotOpenWindows, SnapshotRunningAgents, SpawnBackgroundCommand,
|
SaveProfile, SessionLimitService, SetActiveLayout, SnapshotOpenWindows, SnapshotRunningAgents,
|
||||||
StopLiveAgent, StructuredRoutingMode, StructuredSessions, SuggestedThisSession,
|
SpawnBackgroundCommand, StopLiveAgent, StructuredRoutingMode, StructuredSessions,
|
||||||
SyncAgentWithTemplate, TerminalSessions, TouchDevice, UnassignSkillFromAgent,
|
SuggestedThisSession, SyncAgentWithTemplate, TerminalSessions, TouchDevice,
|
||||||
UnassignTicketFromSprint, UnlinkIssues, UpdateAgentContext, UpdateAgentPermissions,
|
UnassignSkillFromAgent, UnassignTicketFromSprint, UnlinkIssues, UpdateAgentContext,
|
||||||
UpdateIssue, UpdateIssueCarnet, UpdateLiveState, UpdateMemory, UpdateProjectContext,
|
UpdateAgentMcpToolPermissions, UpdateAgentPermissions, UpdateIssue, UpdateIssueCarnet,
|
||||||
|
UpdateLiveState, UpdateMemory, UpdateProjectContext, UpdateProjectMcpToolPermissions,
|
||||||
UpdateProjectPermissions, UpdateSkill, UpdateTemplate, WakeSessionProvider, WriteMemory,
|
UpdateProjectPermissions, UpdateSkill, UpdateTemplate, WakeSessionProvider, WriteMemory,
|
||||||
WriteToTerminal, AGENT_MEMORY_RECALL_BUDGET,
|
WriteToTerminal, AGENT_MEMORY_RECALL_BUDGET,
|
||||||
};
|
};
|
||||||
@ -944,6 +945,12 @@ pub struct BackendCore {
|
|||||||
pub tool_policy_registry: Arc<ToolPolicyRegistry>,
|
pub tool_policy_registry: Arc<ToolPolicyRegistry>,
|
||||||
/// Durable per-agent MCP tool permission store.
|
/// Durable per-agent MCP tool permission store.
|
||||||
pub mcp_tool_permission_store: Arc<dyn McpToolPermissionStore>,
|
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
|
/// Registre des sessions structurées (IA / cellules chat, §17.5). Partagé avec
|
||||||
/// `LaunchAgent`/`ChangeAgentProfile` ; consommé par les commandes de chat (D4)
|
/// `LaunchAgent`/`ChangeAgentProfile` ; consommé par les commandes de chat (D4)
|
||||||
/// pour résoudre la session vivante d'un `sessionId` et l'arrêter à la fermeture.
|
/// 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)));
|
Arc::new(FsMcpToolPermissionStore::new(Arc::clone(&fs_port)));
|
||||||
let mcp_tool_permission_store_port =
|
let mcp_tool_permission_store_port =
|
||||||
Arc::clone(&mcp_tool_permission_store) as Arc<dyn McpToolPermissionStore>;
|
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) ---
|
// --- Skill store (L12) ---
|
||||||
// Global skills live in the machine-local app-data dir; project skills are
|
// Global skills live in the machine-local app-data dir; project skills are
|
||||||
@ -2446,6 +2476,9 @@ impl BackendCore {
|
|||||||
close_ticket_assistant,
|
close_ticket_assistant,
|
||||||
tool_policy_registry,
|
tool_policy_registry,
|
||||||
mcp_tool_permission_store: mcp_tool_permission_store_port,
|
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,
|
structured_sessions,
|
||||||
create_agent,
|
create_agent,
|
||||||
list_agents,
|
list_agents,
|
||||||
|
|||||||
Reference in New Issue
Block a user