Universal Tools API Reference

May 5, 2026 ยท View on GitHub

Complete reference for all 13 universal tools that consolidate 40+ resource-specific operations. Each tool works across multiple resource types using parameter-based routing.

๐Ÿ“ Field Mapping Update: Enhanced with Issue #473 improvements including corrected field mappings, category validation, special character preservation, and response normalization. See Field Mapping Guide for details.

Resource Types

All universal tools use the resource_type parameter to specify the target resource:

enum UniversalResourceType {
  COMPANIES = 'companies',
  PEOPLE = 'people',
  RECORDS = 'records',
  DEALS = 'deals',
  TASKS = 'tasks',
  LISTS = 'lists',
  NOTES = 'notes',
}

search_records, search_records_advanced, search_records_by_timeframe, get_record_details, create_record, update_record, and delete_record also accept custom object slugs discovered into the mapping configuration, such as funds or investment_opportunities.

formatResult Architecture (Updated PR #483)

IMPORTANT: All universal tools now use consistent formatResult functions that always return strings. This eliminates dual-mode behavior and improves performance by 89.7%.

Consistent formatResult Contract

// All formatResult functions follow this pattern
formatResult: (data: AttioRecord | AttioRecord[], resourceType?: UniversalResourceType): string

// Performance optimized with:
// - No environment-dependent behavior
// - Type-safe Record<string, unknown> patterns
// - Memory-efficient string templates
// - 59% ESLint warning reduction (957โ†’395)

Core Universal Tools (8 tools)

1. records.search

Description: Universal search across all resource types with flexible filtering and intelligent query parsing for multi-field lookups.

Consolidates: search-companies, search-people, list-records, list-tasks

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'deals' | 'tasks' | '<custom_object_slug>', // Required
  query?: string,                    // Smart query string (names, emails, phones, domains)
  filters?: object,                  // Advanced filter conditions
  search_type?: 'basic' | 'content', // Search type (default: 'basic')
  fields?: string[],                 // Fields to search (content search only)
  match_type?: 'exact' | 'partial' | 'fuzzy', // Match type (default: 'partial')
  sort?: 'relevance' | 'created' | 'modified' | 'name', // Sort order (default: 'name')
  limit?: number,                    // Max results (1-100, default: 10)
  offset?: number                    // Pagination offset (default: 0)
}

Examples:

// Basic search for companies
await client.callTool('records.search', {
  resource_type: 'companies',
  query: 'tech startup',
  limit: 20,
});

// Multi-field people lookup (name + email handled automatically)
await client.callTool('records.search', {
  resource_type: 'people',
  query: 'Alex Rivera alex.rivera@example.com',
});

// Phone number search (normalizes formatting + country code)
await client.callTool('records.search', {
  resource_type: 'people',
  query: '555-010-4477',
});

// Domain-aware company search (extracts domains from queries/emails)
await client.callTool('records.search', {
  resource_type: 'companies',
  query: 'examplecorp.com',
});

// Content search across multiple fields
await client.callTool('records.search', {
  resource_type: 'companies',
  query: 'artificial intelligence',
  search_type: 'content',
  sort: 'relevance',
});

// Search people with filters
await client.callTool('records.search', {
  resource_type: 'people',
  query: 'john',
  filters: {
    and: [{ attribute: 'industry', condition: 'equals', value: 'Technology' }],
  },
});

// Partial match content search
await client.callTool('records.search', {
  resource_type: 'companies',
  query: 'automat',
  search_type: 'content',
  match_type: 'partial',
});

2. records.get_details

Description: Get detailed information for any record type.

Consolidates: get-company-details, get-person-details, records.get_details, get-task-details

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'deals' | 'tasks' | '<custom_object_slug>', // Required
  record_id: string,                 // Required - Record identifier
  fields?: string[]                  // Optional - Specific fields to include
}

Examples:

// Get company details
await client.callTool('records.get_details', {
  resource_type: 'companies',
  record_id: 'comp_123',
});

// Get custom object details
await client.callTool('records.get_details', {
  resource_type: 'funds',
  record_id: 'fund_123',
});

// Get person details with specific fields
await client.callTool('records.get_details', {
  resource_type: 'people',
  record_id: 'person_456',
  fields: ['name', 'email', 'company'],
});

3. create-record

Description: Create a new record of any supported type.

Consolidates: create-company, create-person, create-record, create-task

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'deals' | 'tasks' | '<custom_object_slug>', // Required
  record_data: object,               // Required - Record creation data
  return_details?: boolean           // Optional - Return full record details
}

Examples:

// Create company
await client.callTool('create-record', {
  resource_type: 'companies',
  record_data: {
    name: 'Acme Corp',
    website: 'https://acme.com',
    industry: 'Technology',
  },
});

// Create person
await client.callTool('create-record', {
  resource_type: 'people',
  record_data: {
    name: 'John Doe',
    email: 'john@acme.com',
    title: 'CEO',
  },
  return_details: true,
});

// Create a custom object record
await client.callTool('create-record', {
  resource_type: 'funds',
  record_data: {
    name: 'Fund I',
    stage: 'Active',
  },
});

4. update-record

Description: Update an existing record of any supported type.

Consolidates: update-company, update-person, update-record, update-task

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'deals' | 'tasks' | '<custom_object_slug>', // Required
  record_id: string,                 // Required - Record identifier
  record_data: object,               // Required - Update data
  return_details?: boolean           // Optional - Return full record details
}

Examples:

// Update company
await client.callTool('update-record', {
  resource_type: 'companies',
  record_id: 'comp_123',
  record_data: {
    industry: 'Fintech',
    employee_count: 50,
  },
});

// Update a custom object record
await client.callTool('update-record', {
  resource_type: 'funds',
  record_id: 'fund_123',
  record_data: {
    stage: 'Closed',
  },
});

5. delete-record

Description: Delete a record of any supported type.

Consolidates: delete-company, delete-person, delete-record, delete-task

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'deals' | 'tasks' | '<custom_object_slug>', // Required
  record_id: string                  // Required - Record identifier
}

Examples:

// Delete company
await client.callTool('delete-record', {
  resource_type: 'companies',
  record_id: 'comp_123',
});

// Delete a custom object record
await client.callTool('delete-record', {
  resource_type: 'funds',
  record_id: 'fund_123',
});

6. records.get_attributes

Description: Get attributes for any resource type.

Consolidates: get-company-attributes, get-person-attributes, get-record-attributes

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
  record_id?: string,                // Optional - Specific record
  categories?: string[],             // Optional - Attribute categories
  fields?: string[]                  // Optional - Specific fields
}

Examples:

// Get all company attributes
await client.callTool('records.get_attributes', {
  resource_type: 'companies',
});

// Get specific record attributes
await client.callTool('records.get_attributes', {
  resource_type: 'people',
  record_id: 'person_456',
  fields: ['name', 'email', 'company'],
});

7. records.discover_attributes

Description: Discover available attributes for any resource type.

Consolidates: discover-company-attributes, discover-person-attributes, discover-record-attributes

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'tasks'; // Required
}

Examples:

// Discover company attributes
await client.callTool('records.discover_attributes', {
  resource_type: 'companies',
});

8. records.get_info

Description: Get specific types of detailed information (contact, business, social).

Consolidates: get-company-basic-info, get-company-contact-info, get-company-business-info, get-company-social-info

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
  record_id: string,                 // Required - Record identifier
  info_type: 'contact' | 'business' | 'social' | 'basic' | 'custom' // Required
}

Examples:

// Get company contact info
await client.callTool('records.get_info', {
  resource_type: 'companies',
  record_id: 'comp_123',
  info_type: 'contact',
});

// Get company business info
await client.callTool('records.get_info', {
  resource_type: 'companies',
  record_id: 'comp_123',
  info_type: 'business',
});

Advanced Universal Tools (5 tools)

9. records.search_advanced

Description: Complex searches with sorting and advanced filtering.

Consolidates: records.search_advanced-companies, records.search_advanced-people

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
  query?: string,                    // Search query string
  filters?: object,                  // Advanced filter conditions
  sort_by?: string,                  // Field to sort by
  sort_order?: 'asc' | 'desc',      // Sort direction
  limit?: number,                    // Max results (1-100, default: 10)
  offset?: number                    // Pagination offset (default: 0)
}

Examples:

// Advanced company search with sorting
await client.callTool('records.search_advanced', {
  resource_type: 'companies',
  query: 'technology',
  filters: {
    and: [
      { attribute: 'employee_count', condition: 'greater_than', value: 100 },
    ],
  },
  sort_by: 'created_at',
  sort_order: 'desc',
  limit: 25,
});

10. records.search_by_relationship

Description: Cross-resource relationship searches.

Consolidates: search-companies-by-people, search-people-by-company

Schema:

{
  relationship_type: 'company_to_people' | 'people_to_company' | 'person_to_tasks' | 'company_to_tasks', // Required
  source_id: string,                 // Required - Source record ID
  target_resource_type?: 'companies' | 'people' | 'records' | 'tasks', // Optional
  limit?: number,                    // Max results (1-100, default: 10)
  offset?: number                    // Pagination offset (default: 0)
}

Examples:

// Find people at a company
await client.callTool('records.search_by_relationship', {
  relationship_type: 'company_to_people',
  source_id: 'comp_123',
  limit: 50,
});

// Find companies associated with a person
await client.callTool('records.search_by_relationship', {
  relationship_type: 'people_to_company',
  source_id: 'person_456',
});

11. records.search_by_content

Description: Content-based searches (notes, activity, interactions).

Consolidates: search-companies-by-notes, search-people-by-notes, search-people-by-activity

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
  content_type: 'notes' | 'activity' | 'interactions',        // Required
  search_query: string,              // Required - Cannot be empty
  limit?: number,                    // Max results (1-100, default: 10)
  offset?: number                    // Pagination offset (default: 0)
}

Examples:

// Search companies by notes
await client.callTool('records.search_by_content', {
  resource_type: 'companies',
  content_type: 'notes',
  search_query: 'quarterly review',
});

// Search people by activity
await client.callTool('records.search_by_content', {
  resource_type: 'people',
  content_type: 'activity',
  search_query: 'demo scheduled',
});

12. records.search_by_timeframe

Description: Time-based searches with date ranges. Supports natural language date expressions (v0.2.1+).

Consolidates: search-people-by-creation-date, search-people-by-modification-date, search-people-by-last-interaction

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
  timeframe_type: 'created' | 'modified' | 'last_interaction', // Required
  start_date?: string,               // ISO 8601 or relative date (e.g., "last 7 days")
  end_date?: string,                 // ISO 8601 or relative date (e.g., "yesterday")
  preset?: string,                   // Date preset or relative expression (e.g., "this_month", "last 30 days")
  limit?: number,                    // Max results (1-100, default: 10)
  offset?: number                    // Pagination offset (default: 0)
}

modified remains part of the public vocabulary for backward compatibility, but Attio does not expose a live filterable modified timestamp for people or companies. Those requests now fail explicitly instead of returning a false empty result.

Supported Date Formats (v0.2.1+):

  • ISO 8601: '2024-01-01T00:00:00Z'
  • Relative expressions: 'today', 'yesterday', 'this week', 'last week', 'this month', 'last month', 'this year', 'last year'
  • Dynamic ranges: 'last N days', 'last N weeks', 'last N months'

Examples:

// Search people created in January 2024 (ISO format)
await client.callTool('records.search_by_timeframe', {
  resource_type: 'people',
  timeframe_type: 'created',
  start_date: '2024-01-01T00:00:00Z',
  end_date: '2024-01-31T23:59:59Z',
});

// Search people created in the last 30 days (natural language)
await client.callTool('records.search_by_timeframe', {
  resource_type: 'people',
  timeframe_type: 'created',
  preset: 'last 30 days',
});

// Search companies with interactions after January 1, 2024
await client.callTool('records.search_by_timeframe', {
  resource_type: 'companies',
  timeframe_type: 'last_interaction',
  start_date: '2024-01-01T00:00:00Z',
});

// Search companies by last interaction using relative dates
await client.callTool('records.search_by_timeframe', {
  resource_type: 'companies',
  timeframe_type: 'last_interaction',
  start_date: 'last 7 days',
  end_date: 'today',
});

13. records.batch

Description: Bulk operations on multiple records.

Consolidates: batch-create-companies, batch-update-companies, batch-delete-companies, records.search_batch-companies, batch-get-company-details, batch-create-records, batch-update-records

Schema:

{
  resource_type: 'companies' | 'people' | 'records' | 'tasks', // Required
  operation_type: 'create' | 'update' | 'delete' | 'search' | 'get', // Required
  records?: Array<object>,           // For create/update operations
  record_ids?: string[],             // For get/delete operations
  query?: string,                    // For search operations (required, cannot be empty)
  limit?: number,                    // Max results (1-50, default: 10)
  offset?: number                    // Pagination offset (default: 0)
}

Examples:

// Batch create companies
await client.callTool('records.batch', {
  resource_type: 'companies',
  operation_type: 'create',
  records: [
    { name: 'Company A', website: 'https://companya.com' },
    { name: 'Company B', website: 'https://companyb.com' },
  ],
});

// Batch get company details
await client.callTool('records.batch', {
  resource_type: 'companies',
  operation_type: 'get',
  record_ids: ['comp_123', 'comp_456', 'comp_789'],
});

// Batch search companies
await client.callTool('records.batch', {
  resource_type: 'companies',
  operation_type: 'search',
  query: 'technology startup',
  limit: 50,
});

Parameter Validation Rules

Required Parameters

  • All tools require resource_type
  • Record operations require record_id
  • Create/update operations require record_data
  • Content searches require non-empty search_query

Date Operators โš ๏ธ

Use correct operators for date filtering:

โœ… Correct:

{
  condition: 'after';
} // Instead of greater_than_or_equals
{
  condition: 'before';
} // Instead of less_than_or_equals

โŒ Incorrect:

{
  condition: 'greater_than_or_equals';
} // Will cause API errors
{
  condition: 'less_than_or_equals';
} // Will cause API errors

Valid Date Presets

('today',
  'yesterday',
  'this_week',
  'last_week',
  'this_month',
  'last_month',
  'this_quarter',
  'last_quarter',
  'this_year',
  'last_year');

// โŒ Invalid: 'last_30_days'

Batch Operation Limits

  • Maximum 50 records per batch operation
  • Built-in rate limiting with 100ms delays
  • Maximum 5 concurrent requests
  • Error isolation - individual failures don't stop the batch

Query Requirements

  • Search queries cannot be empty strings
  • Use meaningful search terms
  • Content searches require specific, non-generic queries

Error Handling

Common Error Types

  1. Invalid Resource Type

    Error: Invalid resource type: 'invalid_type'
    
  2. Missing Required Parameters

    Error: Missing required parameter: record_id
    
  3. Invalid Date Operators

    Error: Invalid operator: $greater_than_or_equals
    
  4. Empty Query Strings

    Error: Search query cannot be empty
    
  5. Invalid Date Presets

    Error: Invalid date preset: "last_30_days"
    

Error Response Format

{
  error: string,           // Error message
  code: string,           // Error code
  resource_type?: string, // Resource type if applicable
  operation?: string      // Operation that failed
}

Testing and Mock Data

Test Environment Behavior

When running in test environment (NODE_ENV=test or VITEST=true), the universal tools automatically use mock data instead of making real API calls:

// Automatic mock data injection in test environment
await client.callTool('create-record', {
  resource_type: 'tasks',
  data: { content: 'Test task' },
});
// Returns: Mock task with proper Attio field format

// Special mock IDs trigger error scenarios
await client.callTool('records.get_details', {
  resource_type: 'tasks',
  record_id: 'mock-error-not-found',
});
// Returns: Error with "Record not found" message

Mock Data Format

Mock data follows the Attio field format with dual access patterns:

{
  id: {
    record_id: 'mock-task-123',
    task_id: 'mock-task-123',      // Resource-specific ID
    workspace_id: 'mock-workspace'
  },
  // Nested format (Attio API standard)
  values: {
    content: [{ value: 'Task content' }],
    status: [{ value: 'pending' }]
  },
  // Flattened format (backward compatibility)
  content: 'Task content',
  status: 'pending'
}

Error Testing

Use special mock IDs to test error handling:

  • mock-error-not-found: Triggers 404 not found error
  • mock-error-unauthorized: Triggers 401 unauthorized error
  • mock-error-invalid: Triggers 400 invalid request error

Performance Guidelines

Pagination

  • Use limit and offset for large result sets
  • Default limit is 10, maximum is 100
  • For batch operations, maximum limit is 50

Batch Operations

  • Prefer batch operations for multiple records
  • Built-in rate limiting prevents API throttling
  • Error isolation ensures partial success handling

Field Selection

  • Use fields parameter to limit returned data
  • Reduces response size and improves performance
  • Especially important for record details

Filtering

  • Use filters for precise searches instead of broad queries
  • Combine multiple conditions with and/or operators
  • Date range filtering is optimized for performance

Next Steps