Tools — Records
Core MCP tools for searching, creating, updating, and deleting CRM records on any object.
The Records tools let your AI assistant perform full CRUD operations on any CRM object — contacts, companies, opportunities, or any custom object you have defined.
crm_find_records
Search, filter, and paginate records from any CRM object.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
object_name | string | Yes | Object name: contacts, companies, opportunities, or any custom object. |
record_id | string | No | Get a single record by UUID. Returns enriched data with display values and relations. |
search | string | No | Full-text search across text fields. |
filter_field | string | No | Simple filter: field name to filter on. |
filter_operator | string | No | Operator: equals, contains, in, greater_than, date_before, etc. |
filter_value | string | No | Value to filter against. Use comma-separated values for in. |
filters | string | No | JSON-encoded FilterGroup for complex AND/OR logic. |
sort | string | No | JSON array for multi-column sort: [{"field":"name","direction":"asc"}]. |
sort_by | string | No | Single sort field. Used as fallback when sort is absent. |
sort_dir | asc | desc | No | Sort direction. |
fields | string | No | Comma-separated field names for projection. |
cursor | string | No | Pagination cursor from a previous response. |
limit | number | No | Max 100, default 20. |
Examples
Get a contact by ID:
{
"object_name": "contacts",
"record_id": "550e8400-e29b-41d4-a716-446655440000"
}The response includes enriched data: display values for foreign keys (e.g., company name), expanded compound fields (EMAILS, PHONES, ADDRESS), and many-to-many relations.
Search contacts by name:
{
"object_name": "contacts",
"search": "Alice Martin",
"limit": 10
}Complex filter with sorting:
Find contacts with status "Lead" or "customer", sorted by name:
{
"object_name": "contacts",
"filters": "{\"type\":\"OR\",\"conditions\":[{\"field\":\"contact_status\",\"operator\":\"equals\",\"value\":\"Lead\"},{\"field\":\"contact_status\",\"operator\":\"equals\",\"value\":\"customer\"}]}",
"sort": "[{\"field\":\"full_name\",\"direction\":\"asc\"}]",
"fields": "full_name,email,contact_status"
}FilterGroup format
The filters parameter accepts a JSON-encoded FilterGroup that supports nested AND/OR logic.
{
"type": "AND",
"conditions": [
{ "field": "full_name", "operator": "contains", "value": "marc" },
{
"type": "OR",
"conditions": [
{ "field": "contact_status", "operator": "equals", "value": "Lead" },
{ "field": "contact_status", "operator": "equals", "value": "customer" }
]
}
]
}This matches records where the name contains "marc" and the status is either "Lead" or "customer". Groups can be nested to any depth.
Response
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"full_name": "Alice Martin",
"email": "alice@example.com",
"contact_status": "customer",
"created_at": "2025-01-15T10:30:00Z"
}
],
"total": 42,
"nextCursor": "eyJpZCI6IjU1MGU4NDAw..."
}When nextCursor is absent or null, you have reached the last page.
crm_save_record
Create or update a record on any object.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
object_name | string | Yes | Target object: contacts, companies, opportunities, or any custom object. |
record_id | string | No | If provided: update the existing record. If absent: create a new record. |
data | object | Yes | Key-value field data to write. |
Automatic behaviors on create
When creating a new record (no record_id), the server applies several defaults automatically:
- Pipeline defaults — If the object has a pipeline, the default pipeline and its first step are assigned.
- USER fields —
owner_idandcreated_by_idare set to the authenticated user. - Field defaults — Default values defined in field metadata are applied for any omitted fields.
Creating relations
Belongs-to-one (foreign key) — Pass the FK field directly in data:
{
"object_name": "contacts",
"data": {
"first_name": "Alice",
"last_name": "Martin",
"entreprise_id": "7a2b3c4d-e5f6-7890-abcd-ef1234567890"
}
}Many-to-many — Pass the relation field as an array of target references:
{
"object_name": "contacts",
"data": {
"first_name": "Alice",
"related_to": [
{ "targetId": "7a2b3c4d-e5f6-7890-abcd-ef1234567890", "_objectType": "contacts" },
{ "targetId": "8b3c4d5e-f6a7-8901-bcde-f12345678901", "_objectType": "companies" }
]
}
}With junction data (e.g. position):
{
"object_name": "contacts",
"data": {
"first_name": "Ken",
"last_name": "Xie",
"companies": [
{
"targetId": "uuid-company",
"junctionData": { "position": "CEO & Chairman" }
}
]
}
}Junction fields are defined per-relation in the schema. Common example: position on the contacts-companies junction.
Examples
Create a contact linked to a company:
{
"object_name": "contacts",
"data": {
"first_name": "Alice",
"last_name": "Martin",
"email": "alice@example.com",
"entreprise_id": "7a2b3c4d-e5f6-7890-abcd-ef1234567890",
"contact_status": "Lead"
}
}Create an opportunity (auto pipeline):
{
"object_name": "opportunities",
"data": {
"name": "Acme Corp renewal",
"amount": 50000,
"close_date": "2025-06-30"
}
}The response includes the auto-assigned pipeline_id and pipeline_step_id.
Update a contact's description:
{
"object_name": "contacts",
"record_id": "550e8400-e29b-41d4-a716-446655440000",
"data": {
"description": "Key decision maker for the enterprise deal."
}
}Response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"action": "created",
"record": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"first_name": "Alice",
"last_name": "Martin",
"email": "alice@example.com",
"entreprise_id": "7a2b3c4d-e5f6-7890-abcd-ef1234567890",
"entreprise_id__display": "Acme Corp",
"contact_status": "Lead",
"owner_id": "user-uuid",
"pipeline_id": "default-pipeline-uuid",
"pipeline_step_id": "first-step-uuid",
"created_at": "2025-01-15T10:30:00Z"
}
}The action field is "created" for new records and "updated" for updates. The record object always contains the full enriched record with display values for relations.
Error handling
Validation error (field-level details):
{
"error": true,
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"field_errors": [
{ "field": "email", "message": "Must be a valid email address" },
{ "field": "first_name", "message": "This field is required" }
]
}Duplicate record (unique constraint violation):
{
"error": true,
"code": "DUPLICATE_RECORD",
"message": "A contact with this email already exists"
}crm_delete_records
Delete one or more records with per-record permission checks.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
object_name | string | Yes | Target object: contacts, companies, opportunities, or any custom object. |
record_ids | string[] | Yes | Array of UUIDs to delete. Max 50 per call. |
Example
Delete two contacts:
{
"object_name": "contacts",
"record_ids": [
"550e8400-e29b-41d4-a716-446655440000",
"661f9511-f30c-52e5-b827-557766551111"
]
}Response
Full success:
{
"deleted": 2,
"failed": 0
}Partial failure (one record could not be deleted):
{
"deleted": 1,
"failed": 1,
"errors": [
{
"id": "661f9511-f30c-52e5-b827-557766551111",
"reason": "Permission denied"
}
]
}Each record is checked individually for permissions. Records that fail are reported in the errors array with the specific reason, while the remaining records are still deleted.
Deletion is permanent. There is no soft-delete or trash. For contacts with synced email accounts, the sync state is cleaned up automatically.
Tips
- The generic records tools (
crm_find_records,crm_save_record,crm_delete_records) also work fortasksandnotesobjects, since they support any CRM object. - For tasks-specific features such as completion toggling and linked entities, use the dedicated
crm_manage_taskstool instead. - For notes-specific features such as visibility controls and @mentions, use the dedicated
crm_manage_notestool instead.