-
Notifications
You must be signed in to change notification settings - Fork 19
Closed
Labels
Description
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
-
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
-
Analyze Architecture
- Review how read operations are implemented
- Check permission system integration
- Understand error handling patterns
- Review XML-RPC operation patterns
-
Check Dependencies
- Verify
OdooConnection
supports write operations - Check
AccessController
permission checking - Review error handling and sanitization
- Confirm logging infrastructure
- Verify
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 checkingErrorSanitizer
for safe error messagestools.py
module for tool registration