""" MCP tool definitions for ActiveBlue AI. All 14 tools are exposed here. Every tool that invokes an agent is FORCED to use the local Ollama LLM regardless of the global privacy_mode setting. This is enforced in server.py before any agent.handle_message() or agent.sweep() call. Tools: dispatch → MasterAgent (routes to best specialist agents) finance_query → FinanceAgent (HIPAA-locked, Ollama only) accounting_query → AccountingAgent (HIPAA-locked, Ollama only) crm_query → CrmAgent sales_query → SalesAgent project_query → ProjectAgent elearning_query → ElearningAgent expenses_query → ExpensesAgent (HIPAA-locked, Ollama only) employees_query → EmployeesAgent (HIPAA-locked, Ollama only) get_health → FastAPI /health/detailed proxy list_agents → AgentRegistry listing trigger_sweep → SweepCoordinator get_pending_approvals → ab_directive_log pending_approval rows approve_directive → approve or reject a pending directive """ from mcp.types import Tool MCP_TOOLS: list[Tool] = [ Tool( name='dispatch', description=( 'Send a natural-language message to the ActiveBlue AI MasterAgent. ' 'The master agent classifies intent and routes to the relevant specialist ' 'agents (finance, CRM, sales, project, etc.) running on local Ollama. ' 'Returns a synthesised reply with agent reports, escalations, and actions taken.' ), inputSchema={ 'type': 'object', 'properties': { 'message': { 'type': 'string', 'description': 'The natural-language request or question', }, 'user_id': { 'type': 'string', 'description': 'User identifier for memory scoping (default: mcp_user)', 'default': 'mcp_user', }, 'context': { 'type': 'object', 'description': 'Optional context dict (partner_id, project_id, etc.)', 'default': {}, }, }, 'required': ['message'], }, ), Tool( name='finance_query', description=( 'Query the Finance Agent directly. Analyses invoices, overdue balances, ' 'collection rates, and payment history. Can send payment reminders and flag ' 'records for review. HIPAA-locked: always uses local Ollama.' ), inputSchema={ 'type': 'object', 'properties': { 'query': {'type': 'string', 'description': 'Finance question or instruction'}, 'partner_id': {'type': 'integer', 'description': 'Filter by Odoo partner ID'}, 'period': {'type': 'string', 'description': 'Period filter e.g. "this_month", "last_quarter"'}, }, 'required': ['query'], }, ), Tool( name='accounting_query', description=( 'Query the Accounting Agent directly. Analyses trial balance, chart of accounts, ' 'journal entries, and tax position. Read-only; never posts entries. ' 'HIPAA-locked: always uses local Ollama.' ), inputSchema={ 'type': 'object', 'properties': { 'query': {'type': 'string', 'description': 'Accounting question or report request'}, 'date_from': {'type': 'string', 'description': 'ISO date YYYY-MM-DD'}, 'date_to': {'type': 'string', 'description': 'ISO date YYYY-MM-DD'}, }, 'required': ['query'], }, ), Tool( name='crm_query', description=( 'Query the CRM Agent directly. Analyses pipeline, opportunities, leads, ' 'won/lost analysis. Can move stages and log activities. Uses local Ollama.' ), inputSchema={ 'type': 'object', 'properties': { 'query': {'type': 'string', 'description': 'CRM question or instruction'}, 'user_id': {'type': 'integer', 'description': 'Filter by salesperson Odoo user ID'}, }, 'required': ['query'], }, ), Tool( name='sales_query', description=( 'Query the Sales Agent directly. Analyses sales orders, quotations, ' 'revenue by rep, expired quotes. Uses local Ollama.' ), inputSchema={ 'type': 'object', 'properties': { 'query': {'type': 'string', 'description': 'Sales question or instruction'}, 'partner_id': {'type': 'integer', 'description': 'Filter by customer partner ID'}, 'date_from': {'type': 'string', 'description': 'ISO date YYYY-MM-DD'}, 'date_to': {'type': 'string', 'description': 'ISO date YYYY-MM-DD'}, }, 'required': ['query'], }, ), Tool( name='project_query', description=( 'Query the Project Agent directly. Analyses tasks, blocked items, overdue ' 'deadlines, and timesheets. Can create tasks and assign them. Uses local Ollama.' ), inputSchema={ 'type': 'object', 'properties': { 'query': {'type': 'string', 'description': 'Project question or instruction'}, 'project_id': {'type': 'integer', 'description': 'Filter by Odoo project ID'}, }, 'required': ['query'], }, ), Tool( name='elearning_query', description=( 'Query the eLearning Agent directly. Analyses course completion rates, ' 'enrolled users, low-engagement courses, and suggests next courses. Uses local Ollama.' ), inputSchema={ 'type': 'object', 'properties': { 'query': {'type': 'string', 'description': 'eLearning question or instruction'}, 'channel_id': {'type': 'integer', 'description': 'Filter by slide.channel ID'}, 'partner_id': {'type': 'integer', 'description': 'Learner partner ID'}, }, 'required': ['query'], }, ), Tool( name='expenses_query', description=( 'Query the Expenses Agent directly. Analyses expense reports, pending approvals, ' 'policy violations. HIPAA-locked: always uses local Ollama.' ), inputSchema={ 'type': 'object', 'properties': { 'query': {'type': 'string', 'description': 'Expenses question or instruction'}, 'employee_id': {'type': 'integer', 'description': 'Filter by Odoo employee ID'}, }, 'required': ['query'], }, ), Tool( name='employees_query', description=( 'Query the Employees (HR) Agent directly. Analyses headcount, contracts, ' 'leave requests, and attendance. HIPAA-locked: always uses local Ollama. ' 'Salary data is never returned to the caller.' ), inputSchema={ 'type': 'object', 'properties': { 'query': {'type': 'string', 'description': 'HR question or instruction'}, 'department_id': {'type': 'integer', 'description': 'Filter by department ID'}, 'employee_id': {'type': 'integer', 'description': 'Filter by employee ID'}, }, 'required': ['query'], }, ), Tool( name='get_health', description='Get detailed health status of the ActiveBlue AI service including DB, Odoo, and Ollama connectivity.', inputSchema={'type': 'object', 'properties': {}, 'required': []}, ), Tool( name='list_agents', description='List all registered AI agents, their domains, active status, and current LLM backend.', inputSchema={'type': 'object', 'properties': {}, 'required': []}, ), Tool( name='trigger_sweep', description=( 'Trigger a proactive sweep across all active agents (or a specific subset). ' 'Agents check for overdue invoices, blocked tasks, expired contracts, etc. ' 'Returns findings and actions taken.' ), inputSchema={ 'type': 'object', 'properties': { 'agents': { 'type': 'array', 'items': {'type': 'string'}, 'description': 'Agent names to sweep. Empty = all active agents.', 'default': [], }, }, 'required': [], }, ), Tool( name='get_pending_approvals', description='List AI directives that are waiting for human approval before proceeding.', inputSchema={'type': 'object', 'properties': {}, 'required': []}, ), Tool( name='approve_directive', description='Approve or reject a pending AI directive. Rejected directives are cancelled.', inputSchema={ 'type': 'object', 'properties': { 'directive_id': {'type': 'string', 'description': 'The directive UUID to respond to'}, 'approved': {'type': 'boolean', 'description': 'True to approve, False to reject'}, 'note': {'type': 'string', 'description': 'Optional reason or note'}, }, 'required': ['directive_id', 'approved'], }, ), ] # Map tool name → agent name for the 8 specialist agent tools AGENT_TOOL_MAP: dict[str, str] = { 'finance_query': 'finance_agent', 'accounting_query': 'accounting_agent', 'crm_query': 'crm_agent', 'sales_query': 'sales_agent', 'project_query': 'project_agent', 'elearning_query': 'elearning_agent', 'expenses_query': 'expenses_agent', 'employees_query': 'employees_agent', }