Skip to main content
Glama
field-mapping-improvements.mdβ€’15.1 kB
# Field Mapping Improvements (Issue #473) This document describes the comprehensive field mapping improvements implemented to resolve inconsistencies in update operations, including category validation, special character handling, and response normalization. ## Table of Contents - [Overview](#overview) - [Fixed Field Mappings](#fixed-field-mappings) - [Category Validation System](#category-validation-system) - [Special Character Handling](#special-character-handling) - [Field Persistence Verification](#field-persistence-verification) - [Response Normalization](#response-normalization) - [Migration Guide](#migration-guide) - [Examples](#examples) ## Overview Issue #473 addressed critical field mapping inconsistencies that caused: - Field updates not persisting after save operations - Fields mapping to incorrect attributes - Inconsistent behavior across different object types - GET operations returning different data than what was updated - Special character corruption in field content ### Key Improvements 1. **Corrected Field Mappings**: Fixed incorrect mappings like `employee_count β†’ estimated_arr` 2. **Category Validation**: Added comprehensive validation with fuzzy matching for company categories 3. **Special Character Preservation**: Implemented sanitization while preserving user intent 4. **Response Normalization**: Standardized response formats across all resource types 5. **Field Persistence Verification**: Added verification to ensure updates persist correctly ## Fixed Field Mappings ### Before (Incorrect Mappings) ```typescript // Previous incorrect mapping const fieldMappings = { employee_count: 'estimated_arr', // ❌ Wrong mapping // ... other mappings }; ``` ### After (Corrected Mappings) ```typescript // Corrected field mappings const FIELD_MAPPINGS = { companies: { // employee_count now maps to itself (correct) employee_count: 'employee_count', // βœ… Correct mapping size: 'estimated_arr', // βœ… Size correctly maps to estimated_arr domain: 'domains', website: 'domains', url: 'domains', // ... other mappings } }; ``` ### Impact - **Data Integrity**: Employee count updates now persist correctly - **Field Collision Prevention**: Eliminated conflicts between employee_count and size fields - **Consistent Behavior**: GET and POST operations now return consistent data ## Category Validation System ### Features The new category validation system includes: 1. **String-to-Array Auto-Conversion**: Automatically converts single category strings to arrays 2. **Fuzzy Matching**: Uses Levenshtein distance to suggest corrections for typos 3. **Case-Insensitive Validation**: Accepts input in any case, returns canonical casing 4. **Comprehensive Error Messages**: Provides helpful "Did you mean?" suggestions ### Usage Examples ```javascript // βœ… Single string auto-converted to array const result1 = await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { categories: 'Technology' // Auto-converted to ['Technology'] } }); // βœ… Array of categories validated const result2 = await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { categories: ['Technology', 'Software', 'SaaS'] } }); // βœ… Case-insensitive with canonical casing const result3 = await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { categories: 'technology' // Returns ['Technology'] } }); ``` ### Error Handling with Suggestions ```javascript // ❌ Typo in category name try { await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { categories: 'Tecnology' // Typo } }); } catch (error) { // Error message: "Invalid category 'Tecnology'. Did you mean 'Technology'?" console.error(error.message); } ``` ### Valid Categories List The system validates against 35+ predefined categories including: ```javascript const VALID_CATEGORIES = [ 'Technology', 'Software', 'SaaS', 'Health Care', 'Financial Services', 'Manufacturing', 'E-commerce', 'Education', 'Construction', 'Automotive', 'Real Estate', 'Media', 'Telecommunications', 'Energy', 'Transportation', 'Food & Beverage', 'Travel & Tourism', 'Entertainment', 'Sports', 'Non-profit', 'Government', 'Legal', 'Consulting', 'Marketing', 'Retail', 'Wholesale', 'Logistics', 'Insurance', 'Banking', 'Investment', 'Biotechnology', 'Pharmaceuticals', 'Agriculture', 'Mining', 'Utilities', 'Finance', 'B2B', 'B2C' ]; ``` ## Special Character Handling ### Preserved Characters The system now properly preserves: - **Quotes and Apostrophes**: `"`, `'`, `"`, `'` - **HTML Entities**: `&amp;`, `&lt;`, `&gt;`, `&quot;`, `&apos;` - **Unicode Characters**: International text, emojis, symbols - **Whitespace**: Newlines (`\n`), tabs (`\t`), spaces - **Special Symbols**: `@`, `#`, `$`, `%`, `&`, `*`, etc. ### Examples ```javascript // βœ… Quotes and apostrophes preserved await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { name: `O'Reilly Media & "Tech" Solutions`, description: `Company with O'Brien's "special" quotes` } }); // βœ… Unicode and emoji support await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { name: 'Tech Company πŸš€ Innovation', description: 'We build πŸ’» solutions with ❀️' } }); // βœ… Multiline content with tabs await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { description: 'Line 1\nLine 2\tTabbed content\nLine 3' } }); ``` ### Sanitization Process The system applies **safe sanitization** that: 1. Preserves all user-intended special characters 2. Maintains formatting (newlines, tabs, etc.) 3. Protects against potential injection while preserving content 4. Ensures consistent encoding across all resource types ## Field Persistence Verification ### Overview Automatic verification ensures that field updates actually persist in the system by: 1. Performing the update operation 2. Fetching the updated record 3. Comparing expected vs actual values 4. Reporting any discrepancies ### Configuration ```javascript // Enable field persistence verification process.env.ENABLE_FIELD_VERIFICATION = 'true'; // Skip verification in test environments process.env.SKIP_FIELD_VERIFICATION = 'true'; ``` ### Verification Process ```javascript // Automatic verification flow const result = await UniversalUpdateService.updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { name: 'Updated Name', employee_count: 150 } }); // System automatically: // 1. Updates the record // 2. Fetches updated record to verify persistence // 3. Compares expected vs actual values // 4. Reports any mismatches in warnings/errors ``` ### Verification Results ```javascript // Successful verification { verified: true, discrepancies: [], warnings: [] } // Failed verification { verified: false, discrepancies: [ 'Field "employee_count" persistence mismatch: expected 150, got 100' ], warnings: [ 'Some fields may not have persisted correctly' ] } ``` ## Response Normalization ### Consistent Response Format All update operations now return a standardized `AttioRecord` format: ```typescript interface AttioRecord { id: { record_id: string; object_id: string; // Added for consistency task_id?: string; // For tasks list_id?: string; // For lists }; values: Record<string, unknown>; created_at?: string; // Added when available updated_at?: string; // Added for tracking } ``` ### Before and After **Before (Inconsistent)**: ```javascript // Different response formats across resource types const companyResult = { id: { record_id: 'comp_123' }, values: {...} }; const taskResult = { id: { task_id: 'task_123' }, content: '...' }; const listResult = { id: { list_id: 'list_123' }, name: '...' }; ``` **After (Normalized)**: ```javascript // Consistent AttioRecord format const companyResult = { id: { record_id: 'comp_123', object_id: 'companies' }, values: { name: 'Company Name', employee_count: 150 }, created_at: '2024-01-01T00:00:00Z', updated_at: '2024-01-15T10:30:00Z' }; const taskResult = { id: { record_id: 'task_123', task_id: 'task_123', object_id: 'tasks' }, values: { content: 'Task Content', title: 'Task Content' }, // Issue #480 compatibility created_at: '2024-01-01T00:00:00Z', updated_at: '2024-01-15T10:30:00Z' }; ``` ### Resource-Specific Normalization Each resource type has specialized normalization: ```javascript // Companies: domains converted to arrays { values: { name: 'Company Name', domains: ['example.com'], // Always array categories: ['Technology'] // Always array } } // People: email_addresses and phone_numbers as arrays { values: { name: 'John Doe', email_addresses: ['john@example.com'], // Always array phone_numbers: ['+1234567890'] // Always array } } // Tasks: dual field support for Issue #480 compatibility { values: { content: 'Task description', title: 'Task description' // Duplicate for compatibility } } ``` ## Migration Guide ### Updating Code for New Field Mappings **Before**: ```javascript // Old code expecting employee_count to map to estimated_arr await updateCompany(id, { employee_count: 100, // This would incorrectly map to estimated_arr size: 'Large' // This would conflict }); ``` **After**: ```javascript // New code with correct mappings await updateCompany(id, { employee_count: 100, // βœ… Maps to employee_count field size: 'Large' // βœ… Maps to estimated_arr field (no conflict) }); ``` ### Handling Categories **Before**: ```javascript // Manual category handling await updateCompany(id, { categories: ['Technology'] // Had to provide as array }); ``` **After**: ```javascript // Auto-conversion support await updateCompany(id, { categories: 'Technology' // βœ… Auto-converted to ['Technology'] }); // Or still use arrays await updateCompany(id, { categories: ['Technology', 'Software'] // βœ… Still works }); ``` ### Response Format Updates **Before**: ```javascript // Responses varied by resource type const result = await updateRecord({...}); // result.id might have different properties // result.values might be at different levels ``` **After**: ```javascript // Consistent response format const result = await updateRecord({...}); // Always has: result.id.record_id, result.id.object_id // Always has: result.values (normalized structure) // May have: result.created_at, result.updated_at ``` ## Examples ### Complete Update with All Features ```javascript const result = await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { name: 'O\'Reilly Media & "Tech" Solutions πŸš€', employee_count: 250, // Correct field mapping categories: 'Technology', // Auto-converted to array description: 'Multi-line\ndescription\twith\tspecial chars', website: 'https://oreilly.com', industry: 'Software & Media' } }); // Normalized response console.log(result); // { // id: { // record_id: 'comp_123', // object_id: 'companies' // }, // values: { // name: 'O\'Reilly Media & "Tech" Solutions πŸš€', // employee_count: 250, // categories: ['Technology'], // description: 'Multi-line\ndescription\twith\tspecial chars', // domains: ['https://oreilly.com'], // industry: 'Software & Media' // }, // updated_at: '2024-01-15T10:30:00Z' // } ``` ### Error Handling with Improved Messages ```javascript try { await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { categories: 'Tecnology', // Typo employee_count: 'not_a_number' // Wrong type } }); } catch (error) { console.error(error.message); // "Invalid category 'Tecnology'. Did you mean 'Technology'?" // "Field validation failed: employee_count must be a number" } ``` ### Field Collision Prevention ```javascript // ❌ Before: This would cause field collision await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { employee_count: 100, // Would map to estimated_arr (wrong) size: 'Large' // Would also map to estimated_arr (collision) } }); // βœ… After: No collision, correct mappings await updateRecord({ resource_type: 'companies', record_id: 'comp_123', record_data: { employee_count: 100, // Maps to employee_count (correct) size: 'Large' // Maps to estimated_arr (correct) } }); ``` ## Testing ### Comprehensive Test Coverage The improvements include extensive test coverage: 1. **Unit Tests**: Field mapping validation, category validation, special character handling 2. **Integration Tests**: Real API validation, end-to-end workflows 3. **QA Tests**: Complete Issue #473 scenario validation 4. **Regression Tests**: Prevent reintroduction of mapping issues ### Running Tests ```bash # Run all tests related to field mapping npm test test/unit/category-validation.test.ts npm test test/integration/special-character-handling.test.ts npm test test/integration/issue-473-comprehensive-qa.test.ts # Run specific field collision tests npm test test/unit/field-collision-detection.test.ts # Run full test suite npm test ``` ## Best Practices 1. **Use Auto-Conversion**: Take advantage of category string-to-array conversion 2. **Validate Input**: Let the system validate categories and provide suggestions 3. **Preserve Characters**: Don't pre-sanitize special characters, let the system handle it 4. **Monitor Verification**: Enable field persistence verification in development 5. **Handle Errors**: Implement proper error handling for validation failures ## Troubleshooting ### Common Issues 1. **Category Validation Errors** ``` Error: Invalid category "Healthcare". Did you mean "Health Care"? ``` **Solution**: Use the suggested category name or check valid categories list 2. **Field Collision Detected** ``` Error: Field collision detected: "domain", "website" all map to "domains" ``` **Solution**: Use only one field that maps to the target, prefer the canonical field name 3. **Special Characters Lost** ``` Expected: O'Reilly & "Tech" Got: OReilly Tech ``` **Solution**: Update to latest version with special character preservation ### Getting Help - Check the [Troubleshooting Guide](../universal-tools/troubleshooting.md) - Review test cases for examples - Create an issue with reproduction steps ## See Also - [Universal Tools API Reference](../universal-tools/api-reference.md) - [Field Validation Rules](./field-validation-rules.md) - [Category Validation Tests](../../test/unit/category-validation.test.ts) - [Issue #473 Implementation](https://github.com/kesslerio/attio-mcp-server/pull/505)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kesslerio/attio-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server