Skip to content

[FEATURE] Implement MCP prompts for guided workflows #7

@ivnvxd

Description

@ivnvxd

Implement MCP Prompts

Feature Description

Implement MCP prompts to provide guided, interactive workflows for common Odoo operations. Prompts help users through complex tasks by asking for information step-by-step, validating inputs, and suggesting options.

Motivation

Many Odoo operations require multiple fields and have complex validation rules. Prompts can:

  • Guide users through required fields
  • Validate data before submission
  • Suggest related records
  • Provide context-aware help
  • Reduce errors and improve data quality

Proposed Prompts

1. Customer Creation Prompt

@mcp.prompt()
async def create_customer_prompt() -> Dict[str, Any]:
    """Interactive prompt for creating a new customer."""
    
    return {
        "name": "create_customer",
        "description": "Create a new customer with guided input",
        "parameters": [
            {
                "name": "company_name",
                "description": "Company name (leave empty for individual)",
                "type": "string",
                "required": False
            },
            {
                "name": "contact_name", 
                "description": "Contact person name",
                "type": "string",
                "required": True
            },
            {
                "name": "email",
                "description": "Email address",
                "type": "string",
                "pattern": r"^[\w\.-]+@[\w\.-]+\.\w+$",
                "required": True
            },
            {
                "name": "phone",
                "description": "Phone number",
                "type": "string",
                "required": False
            },
            {
                "name": "country",
                "description": "Country",
                "type": "selection",
                "options": await self._get_country_options(),
                "required": True
            },
            {
                "name": "tags",
                "description": "Customer tags",
                "type": "multi-selection",
                "options": await self._get_tag_options(),
                "required": False
            }
        ],
        "on_submit": "create_customer_from_prompt"
    }

2. Product Search Assistant

@mcp.prompt()
async def product_search_prompt() -> Dict[str, Any]:
    """Smart product search with filters."""
    
    return {
        "name": "product_search",
        "description": "Search products with smart filters",
        "parameters": [
            {
                "name": "search_term",
                "description": "Product name or reference",
                "type": "string",
                "required": False
            },
            {
                "name": "category",
                "description": "Product category",
                "type": "selection",
                "options": await self._get_category_tree(),
                "required": False
            },
            {
                "name": "price_range",
                "description": "Price range",
                "type": "range",
                "min": 0,
                "max": 10000,
                "step": 10,
                "required": False
            },
            {
                "name": "in_stock_only",
                "description": "Only show products in stock",
                "type": "boolean",
                "default": True
            },
            {
                "name": "attributes",
                "description": "Product attributes",
                "type": "dynamic",
                "handler": "get_attribute_filters"
            }
        ],
        "preview": True,  # Show results preview before final selection
        "on_submit": "search_products_from_prompt"
    }

3. Data Import Wizard

@mcp.prompt()
async def import_data_prompt() -> Dict[str, Any]:
    """Import data from CSV/Excel with field mapping."""
    
    return {
        "name": "import_data",
        "description": "Import records from file with field mapping",
        "steps": [
            {
                "name": "file_selection",
                "parameters": [{
                    "name": "file_content",
                    "description": "Paste CSV content or file path",
                    "type": "text",
                    "required": True
                }]
            },
            {
                "name": "model_selection",
                "parameters": [{
                    "name": "model",
                    "description": "Target Odoo model",
                    "type": "selection",
                    "options": await self._get_writable_models(),
                    "required": True
                }]
            },
            {
                "name": "field_mapping",
                "parameters": "dynamic",  # Generated based on CSV headers
                "handler": "generate_field_mapping"
            },
            {
                "name": "validation_options",
                "parameters": [
                    {
                        "name": "skip_errors",
                        "description": "Skip rows with errors",
                        "type": "boolean",
                        "default": True
                    },
                    {
                        "name": "update_existing",
                        "description": "Update existing records",
                        "type": "boolean",
                        "default": False
                    }
                ]
            }
        ],
        "on_submit": "import_data_from_prompt",
        "supports_progress": True
    }

4. Relationship Explorer

@mcp.prompt()
async def explore_relationships_prompt() -> Dict[str, Any]:
    """Explore record relationships interactively."""
    
    return {
        "name": "explore_relationships",
        "description": "Navigate through record relationships",
        "parameters": [
            {
                "name": "starting_point",
                "description": "Start from model/record",
                "type": "model_record_selector",
                "required": True
            },
            {
                "name": "relationship_depth",
                "description": "How many levels to explore",
                "type": "integer",
                "min": 1,
                "max": 5,
                "default": 2
            },
            {
                "name": "relationship_types",
                "description": "Types of relationships to follow",
                "type": "multi-selection",
                "options": [
                    "many2one",
                    "one2many", 
                    "many2many"
                ],
                "default": ["many2one", "one2many"]
            }
        ],
        "interactive": True,  # Allow drilling down during exploration
        "on_submit": "explore_from_prompt"
    }

Implementation Requirements

Prompt Registration

# In prompts.py
class OdooMCPPrompts:
    def __init__(self, server: 'OdooMCPServer'):
        self.server = server
        self.app = server.app
        self._register_prompts()
    
    def _register_prompts(self):
        """Register all prompts with FastMCP."""
        self.app.prompt(self.create_customer_prompt)
        self.app.prompt(self.product_search_prompt)
        self.app.prompt(self.import_data_prompt)
        self.app.prompt(self.explore_relationships_prompt)

Validation Framework

class PromptValidator:
    """Validate prompt inputs before processing."""
    
    async def validate_email(self, value: str) -> bool:
        """Validate email format."""
        pattern = r"^[\w\.-]+@[\w\.-]+\.\w+$"
        return bool(re.match(pattern, value))
    
    async def validate_phone(self, value: str) -> bool:
        """Validate phone number format."""
        # Implementation depends on country
        pass
    
    async def validate_selection(self, value: str, options: List[str]) -> bool:
        """Ensure selection is from allowed options."""
        return value in options

Progress Support

class ProgressReporter:
    """Report progress for long operations."""
    
    async def report_import_progress(self, current: int, total: int, message: str):
        """Send progress update to client."""
        await self.app.report_progress({
            "operation": "import_data",
            "current": current,
            "total": total,
            "percentage": (current / total) * 100,
            "message": message
        })

Success Criteria

  • At least 4 prompts implemented
  • Input validation works correctly
  • Dynamic options loaded from Odoo
  • Progress reporting for long operations
  • Error handling with helpful messages
  • Integration tests for each prompt
  • Documentation with examples
  • Prompt discovery through MCP protocol

Benefits

  1. User Experience: Guided workflows reduce errors
  2. Data Quality: Validation ensures clean data
  3. Efficiency: Batch operations with progress tracking
  4. Discoverability: Users can explore available operations
  5. Integration: Works with any MCP-compatible client

Testing Requirements

  • Unit tests for validation logic
  • Integration tests for each prompt
  • Test dynamic option loading
  • Test progress reporting
  • Test error scenarios
  • Test with various MCP clients

Documentation

Create documentation covering:

  • How to use each prompt
  • Examples of prompt interactions
  • How to extend with custom prompts
  • Integration with MCP clients
  • Best practices for prompt design

Priority

Medium - While not critical for basic functionality, prompts significantly improve user experience and reduce errors in complex operations.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions