Skip to content

[FEATURE] Implement write operations - create_record, update_record, delete_record #5

@ivnvxd

Description

@ivnvxd

Implement Write Operations

Feature Description

Implement the missing write operations to complete CRUD functionality. Currently, the MCP server only supports read operations. This represents the final 10% of the project implementation.

Motivation

Users need to:

  • Create new records in Odoo (e.g., create customers, products, orders)
  • Update existing records (e.g., change addresses, update prices)
  • Delete records when appropriate (with permission checks)

Without these operations, the MCP server is read-only, limiting its usefulness for AI-assisted data management.

Before Implementation

⚠️ CRITICAL: Check current implementation status before proceeding

  1. Verify Current State

    • Check if write operations are partially implemented
    • Review existing tool registration in tools.py
    • Check if create/update/delete methods exist in connection layer
    • Test current behavior with write operations
  2. Analyze Architecture

    • Review how read operations are implemented
    • Check permission system integration
    • Understand error handling patterns
    • Review XML-RPC operation patterns
  3. Check Dependencies

    • Verify OdooConnection supports write operations
    • Check AccessController permission checking
    • Review error handling and sanitization
    • Confirm logging infrastructure

Proposed Implementation Approach

Note: These are conceptual examples - check actual codebase structure first!

1. create_record Tool (Conceptual)

# PSEUDOCODE - Check actual tool registration patterns first!
@tool_decorator
async def create_record(model: str, values: dict) -> dict:
    """Create a new record in an Odoo model."""
    # 1. Check permissions
    # 2. Validate field values
    # 3. Call Odoo create operation
    # 4. Return formatted result

2. update_record Tool (Conceptual)

# PSEUDOCODE - Check actual tool patterns first!
@tool_decorator
async def update_record(model: str, record_id: int, values: dict) -> dict:
    """Update an existing record."""
    # 1. Check permissions for write operation
    # 2. Validate record exists
    # 3. Validate field values
    # 4. Call Odoo write operation
    # 5. Return update confirmation

3. delete_record Tool (Conceptual)

# PSEUDOCODE - Check actual tool patterns first!
@tool_decorator  
async def delete_record(model: str, record_id: int) -> dict:
    """Delete a record (if user has unlink permission)."""
    # 1. Check unlink permissions
    # 2. Validate record exists
    # 3. Call Odoo unlink operation
    # 4. Return deletion confirmation

Implementation Requirements

Permission Checking

  • Verify user has create/write/unlink permissions for the model
  • Use existing AccessController to check permissions
  • Return clear error messages for permission denials

Field Validation

  • Validate required fields are provided
  • Check field types match expected types
  • Handle special field types:
    • Many2one: Accept ID or [id, name] format
    • One2many/Many2many: Accept list of IDs or command tuples
    • Date/Datetime: Accept ISO format strings

Error Handling

try:
    # Check permissions
    if not await self._check_model_access(model, operation):
        raise PermissionError(f"No {operation} permission for model {model}")
    
    # Validate fields
    fields_info = await self.connection.fields_get(model)
    validation_errors = self._validate_values(values, fields_info)
    if validation_errors:
        raise ValueError(f"Field validation errors: {validation_errors}")
    
    # Perform operation
    result = await self.connection.create(model, values)
    
except Exception as e:
    return {
        "error": self.error_sanitizer.sanitize_error_message(str(e)),
        "type": "validation_error" if "Field" in str(e) else "permission_error"
    }

Test Cases

create_record Tests

  • Create simple record with required fields only
  • Create record with all field types (char, integer, float, boolean, date, etc.)
  • Create record with Many2one relationship
  • Create record with One2many/Many2many relationships
  • Verify permission denial for restricted models
  • Validate required field enforcement
  • Test field type validation

update_record Tests

  • Update single field
  • Update multiple fields
  • Update relational fields
  • Verify only specified fields are changed
  • Test permission checks
  • Test non-existent record handling

delete_record Tests

  • Delete existing record
  • Verify unlink permission required
  • Test cascading deletes
  • Handle non-existent record
  • Verify audit logging

Examples

Creating a Customer

result = await create_record(
    model="res.partner",
    values={
        "name": "Acme Corporation",
        "is_company": True,
        "email": "[email protected]",
        "phone": "+1-555-0123",
        "street": "123 Main St",
        "city": "San Francisco",
        "country_id": 233,  # United States
        "customer_rank": 1
    }
)
# Returns: {"id": 42, "model": "res.partner", "url": "http://localhost:8069/web#id=42&model=res.partner"}

Updating a Product Price

result = await update_record(
    model="product.product",
    record_id=15,
    values={
        "list_price": 299.99,
        "standard_price": 150.00
    }
)
# Returns: {"id": 15, "model": "product.product", "updated_fields": ["list_price", "standard_price"]}

Success Criteria

  • All three tools are implemented and registered
  • Permission checks work correctly
  • Field validation prevents invalid data
  • Error messages are clear and sanitized
  • Operations are logged in mcp.log
  • Comprehensive test coverage (>90%)
  • Documentation updated with examples
  • Integration tests verify end-to-end functionality

Dependencies

  • Existing OdooConnection class for XML-RPC operations
  • AccessController for permission checking
  • ErrorSanitizer for safe error messages
  • tools.py module for tool registration

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions