Twenty MCP by usefulapi

Read people, companies, opportunities, notes and tasks; create and update records in Twenty CRM.

Should I use this

Quality & Safety

A
Description quality
94%
Schema completeness
94%
Naming quality
80%
Poisoning risk
100%
Permission match
100%
Protocol compliance
100%

Based on automated analysis of tool definitions and protocol compliance.

Context Cost

~4,584Tokens (tool definitions)
~2.1 KBTypical response size
Significant attention impact (3.58% of 128k context)

This is the approximate number of tokens consumed each time the server's tools are loaded into a model's context. Higher counts reduce the attention available for other tasks.

Install

One-Click Install

Add this to your `claude_desktop_config.json` file:

{
  "mcpServers": {
    "twenty": {
      "url": "https://twenty.usefulapi.io/mcp"
    }
  }
}

Remote endpoints

https://twenty.usefulapi.io/mcpstreamable-http

What it can do

Tool inventory

Tools (20)

🟢 Read-only🟡 Write🔴 Delete⚪ Unknown
🟢twenty_list_metadata_objects

List all objects (standard + custom) and their fields from the Twenty schema — use this to discover custom objects/fields and valid field names. Twenty REST: GET /rest/metadata/objects.

Input Schema

{
  "type": "object",
  "properties": {},
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢twenty_list_people(limit, starting_after, ending_before, filter, order_by, ...)

List/filter people (contacts). Supports cursor pagination, raw filter/order_by, and relation depth. Twenty REST: GET /rest/people.

Input Schema

{
  "type": "object",
  "properties": {
    "limit": {
      "description": "Results per page (1-60, default 20 — Twenty caps at 60).",
      "type": "integer",
      "minimum": 1,
      "maximum": 60
    },
    "starting_after": {
      "description": "Pagination cursor — return records after this cursor (from a previous page's pageInfo).",
      "type": "string"
    },
    "ending_before": {
      "description": "Pagination cursor — return records before this cursor (from a previous page's pageInfo).",
      "type": "string"
    },
    "filter": {
      "description": "Raw Twenty filter string, passed verbatim as ?filter=. Syntax: field[comparator]:value, e.g. `employees[gte]:100` or `stage[eq]:\"MEETING\"`. Comparators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith.",
      "type": "string"
    },
    "order_by": {
      "description": "Raw Twenty ordering string, passed verbatim as ?order_by=, e.g. `createdAt[DescNullsLast]`.",
      "type": "string"
    },
    "depth": {
      "description": "How deep to hydrate related records: 0, 1, or 2.",
      "type": "integer",
      "minimum": 0,
      "maximum": 2
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢twenty_get_person(id, depth)

Get a single person (contact) by id. Twenty REST: GET /rest/people/{id}.

Input Schema

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "The person id."
    },
    "depth": {
      "description": "How deep to hydrate related records: 0, 1, or 2.",
      "type": "integer",
      "minimum": 0,
      "maximum": 2
    }
  },
  "required": [
    "id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢twenty_list_companies(limit, starting_after, ending_before, filter, order_by, ...)

List/filter companies. Supports cursor pagination, raw filter/order_by, and relation depth. Twenty REST: GET /rest/companies.

Input Schema

{
  "type": "object",
  "properties": {
    "limit": {
      "description": "Results per page (1-60, default 20 — Twenty caps at 60).",
      "type": "integer",
      "minimum": 1,
      "maximum": 60
    },
    "starting_after": {
      "description": "Pagination cursor — return records after this cursor (from a previous page's pageInfo).",
      "type": "string"
    },
    "ending_before": {
      "description": "Pagination cursor — return records before this cursor (from a previous page's pageInfo).",
      "type": "string"
    },
    "filter": {
      "description": "Raw Twenty filter string, passed verbatim as ?filter=. Syntax: field[comparator]:value, e.g. `employees[gte]:100` or `stage[eq]:\"MEETING\"`. Comparators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith.",
      "type": "string"
    },
    "order_by": {
      "description": "Raw Twenty ordering string, passed verbatim as ?order_by=, e.g. `createdAt[DescNullsLast]`.",
      "type": "string"
    },
    "depth": {
      "description": "How deep to hydrate related records: 0, 1, or 2.",
      "type": "integer",
      "minimum": 0,
      "maximum": 2
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢twenty_get_company(id, depth)

Get a single company by id. Twenty REST: GET /rest/companies/{id}.

Input Schema

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "The company id."
    },
    "depth": {
      "description": "How deep to hydrate related records: 0, 1, or 2.",
      "type": "integer",
      "minimum": 0,
      "maximum": 2
    }
  },
  "required": [
    "id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢twenty_list_opportunities(limit, starting_after, ending_before, filter, order_by, ...)

List/filter opportunities (deals). Supports cursor pagination, raw filter/order_by, and relation depth. Twenty REST: GET /rest/opportunities.

Input Schema

{
  "type": "object",
  "properties": {
    "limit": {
      "description": "Results per page (1-60, default 20 — Twenty caps at 60).",
      "type": "integer",
      "minimum": 1,
      "maximum": 60
    },
    "starting_after": {
      "description": "Pagination cursor — return records after this cursor (from a previous page's pageInfo).",
      "type": "string"
    },
    "ending_before": {
      "description": "Pagination cursor — return records before this cursor (from a previous page's pageInfo).",
      "type": "string"
    },
    "filter": {
      "description": "Raw Twenty filter string, passed verbatim as ?filter=. Syntax: field[comparator]:value, e.g. `employees[gte]:100` or `stage[eq]:\"MEETING\"`. Comparators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith.",
      "type": "string"
    },
    "order_by": {
      "description": "Raw Twenty ordering string, passed verbatim as ?order_by=, e.g. `createdAt[DescNullsLast]`.",
      "type": "string"
    },
    "depth": {
      "description": "How deep to hydrate related records: 0, 1, or 2.",
      "type": "integer",
      "minimum": 0,
      "maximum": 2
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢twenty_get_opportunity(id, depth)

Get a single opportunity by id. Twenty REST: GET /rest/opportunities/{id}.

Input Schema

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "The opportunity id."
    },
    "depth": {
      "description": "How deep to hydrate related records: 0, 1, or 2.",
      "type": "integer",
      "minimum": 0,
      "maximum": 2
    }
  },
  "required": [
    "id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢twenty_list_notes(limit, starting_after, ending_before, filter, order_by, ...)

List/filter notes. Supports cursor pagination, raw filter/order_by, and relation depth. Twenty REST: GET /rest/notes.

Input Schema

{
  "type": "object",
  "properties": {
    "limit": {
      "description": "Results per page (1-60, default 20 — Twenty caps at 60).",
      "type": "integer",
      "minimum": 1,
      "maximum": 60
    },
    "starting_after": {
      "description": "Pagination cursor — return records after this cursor (from a previous page's pageInfo).",
      "type": "string"
    },
    "ending_before": {
      "description": "Pagination cursor — return records before this cursor (from a previous page's pageInfo).",
      "type": "string"
    },
    "filter": {
      "description": "Raw Twenty filter string, passed verbatim as ?filter=. Syntax: field[comparator]:value, e.g. `employees[gte]:100` or `stage[eq]:\"MEETING\"`. Comparators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith.",
      "type": "string"
    },
    "order_by": {
      "description": "Raw Twenty ordering string, passed verbatim as ?order_by=, e.g. `createdAt[DescNullsLast]`.",
      "type": "string"
    },
    "depth": {
      "description": "How deep to hydrate related records: 0, 1, or 2.",
      "type": "integer",
      "minimum": 0,
      "maximum": 2
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢twenty_get_note(id, depth)

Get a single note by id. Twenty REST: GET /rest/notes/{id}.

Input Schema

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "The note id."
    },
    "depth": {
      "description": "How deep to hydrate related records: 0, 1, or 2.",
      "type": "integer",
      "minimum": 0,
      "maximum": 2
    }
  },
  "required": [
    "id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢twenty_list_tasks(limit, starting_after, ending_before, filter, order_by, ...)

List/filter tasks. Supports cursor pagination, raw filter/order_by, and relation depth. Twenty REST: GET /rest/tasks.

Input Schema

{
  "type": "object",
  "properties": {
    "limit": {
      "description": "Results per page (1-60, default 20 — Twenty caps at 60).",
      "type": "integer",
      "minimum": 1,
      "maximum": 60
    },
    "starting_after": {
      "description": "Pagination cursor — return records after this cursor (from a previous page's pageInfo).",
      "type": "string"
    },
    "ending_before": {
      "description": "Pagination cursor — return records before this cursor (from a previous page's pageInfo).",
      "type": "string"
    },
    "filter": {
      "description": "Raw Twenty filter string, passed verbatim as ?filter=. Syntax: field[comparator]:value, e.g. `employees[gte]:100` or `stage[eq]:\"MEETING\"`. Comparators: eq, neq, gt, gte, lt, lte, in, is, like, ilike, startsWith.",
      "type": "string"
    },
    "order_by": {
      "description": "Raw Twenty ordering string, passed verbatim as ?order_by=, e.g. `createdAt[DescNullsLast]`.",
      "type": "string"
    },
    "depth": {
      "description": "How deep to hydrate related records: 0, 1, or 2.",
      "type": "integer",
      "minimum": 0,
      "maximum": 2
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🟢twenty_get_task(id, depth)

Get a single task by id. Twenty REST: GET /rest/tasks/{id}.

Input Schema

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "The task id."
    },
    "depth": {
      "description": "How deep to hydrate related records: 0, 1, or 2.",
      "type": "integer",
      "minimum": 0,
      "maximum": 2
    }
  },
  "required": [
    "id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🔴twenty_create_person(firstName, lastName, jobTitle, city, primaryEmail, ...)

Create a new person (contact). All fields optional; firstName/lastName combine into name:{firstName,lastName}. Twenty REST: POST /rest/people.

Input Schema

{
  "type": "object",
  "properties": {
    "firstName": {
      "description": "First name (combined into name:{firstName,lastName}).",
      "type": "string"
    },
    "lastName": {
      "description": "Last name (combined into name:{firstName,lastName}).",
      "type": "string"
    },
    "jobTitle": {
      "description": "Job title.",
      "type": "string"
    },
    "city": {
      "description": "City.",
      "type": "string"
    },
    "primaryEmail": {
      "description": "Primary email address (sent as emails:{primaryEmail}).",
      "type": "string"
    },
    "primaryPhone": {
      "description": "Primary phone number (sent as phones:{primaryPhoneNumber}).",
      "type": "string"
    },
    "companyId": {
      "description": "Id of the company to link this person to.",
      "type": "string"
    },
    "linkedinUrl": {
      "description": "LinkedIn URL (sent as linkedinLink:{primaryLinkUrl}).",
      "type": "string"
    },
    "xUrl": {
      "description": "X/Twitter URL (sent as xLink:{primaryLinkUrl}).",
      "type": "string"
    },
    "extra": {
      "description": "Passthrough object of additional/custom fields merged into the request body verbatim.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🔴twenty_update_person(id, firstName, lastName, jobTitle, city, ...)

Update fields on an existing person. Only provided fields change. Twenty REST: PATCH /rest/people/{id}.

Input Schema

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "The person id to update."
    },
    "firstName": {
      "description": "First name (combined into name:{firstName,lastName}).",
      "type": "string"
    },
    "lastName": {
      "description": "Last name (combined into name:{firstName,lastName}).",
      "type": "string"
    },
    "jobTitle": {
      "description": "Job title.",
      "type": "string"
    },
    "city": {
      "description": "City.",
      "type": "string"
    },
    "primaryEmail": {
      "description": "Primary email address (sent as emails:{primaryEmail}).",
      "type": "string"
    },
    "primaryPhone": {
      "description": "Primary phone number (sent as phones:{primaryPhoneNumber}).",
      "type": "string"
    },
    "companyId": {
      "description": "Id of the company to link this person to.",
      "type": "string"
    },
    "linkedinUrl": {
      "description": "LinkedIn URL (sent as linkedinLink:{primaryLinkUrl}).",
      "type": "string"
    },
    "xUrl": {
      "description": "X/Twitter URL (sent as xLink:{primaryLinkUrl}).",
      "type": "string"
    },
    "extra": {
      "description": "Passthrough object of additional/custom fields merged into the request body verbatim.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "required": [
    "id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🔴twenty_create_company(name, domainName, employees, addressCity, idealCustomerProfile, ...)

Create a new company. `name` is required. Twenty REST: POST /rest/companies.

Input Schema

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The company name (required)."
    },
    "domainName": {
      "description": "Primary domain / website URL (sent as domainName:{primaryLinkUrl}).",
      "type": "string"
    },
    "employees": {
      "description": "Employee count.",
      "type": "number"
    },
    "addressCity": {
      "description": "City (sent as address:{addressCity}).",
      "type": "string"
    },
    "idealCustomerProfile": {
      "description": "Whether this company matches the ideal customer profile.",
      "type": "boolean"
    },
    "linkedinUrl": {
      "description": "LinkedIn URL (sent as linkedinLink:{primaryLinkUrl}).",
      "type": "string"
    },
    "xUrl": {
      "description": "X/Twitter URL (sent as xLink:{primaryLinkUrl}).",
      "type": "string"
    },
    "extra": {
      "description": "Passthrough object of additional/custom fields merged into the request body verbatim.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "required": [
    "name"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🔴twenty_update_company(id, name, domainName, employees, addressCity, ...)

Update fields on an existing company. Only provided fields change. Twenty REST: PATCH /rest/companies/{id}.

Input Schema

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "The company id to update."
    },
    "name": {
      "description": "The company name.",
      "type": "string"
    },
    "domainName": {
      "description": "Primary domain / website URL (sent as domainName:{primaryLinkUrl}).",
      "type": "string"
    },
    "employees": {
      "description": "Employee count.",
      "type": "number"
    },
    "addressCity": {
      "description": "City (sent as address:{addressCity}).",
      "type": "string"
    },
    "idealCustomerProfile": {
      "description": "Whether this company matches the ideal customer profile.",
      "type": "boolean"
    },
    "linkedinUrl": {
      "description": "LinkedIn URL (sent as linkedinLink:{primaryLinkUrl}).",
      "type": "string"
    },
    "xUrl": {
      "description": "X/Twitter URL (sent as xLink:{primaryLinkUrl}).",
      "type": "string"
    },
    "extra": {
      "description": "Passthrough object of additional/custom fields merged into the request body verbatim.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "required": [
    "id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🔴twenty_create_opportunity(name, amountMicros, currencyCode, stage, closeDate, ...)

Create a new opportunity (deal). `name` is required; amountMicros combines with currencyCode into amount:{amountMicros,currencyCode}. Twenty REST: POST /rest/opportunities.

Input Schema

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "The opportunity name (required)."
    },
    "amountMicros": {
      "description": "Opportunity amount in micros (× 1,000,000). Sent as amount:{amountMicros,currencyCode}.",
      "type": "number"
    },
    "currencyCode": {
      "default": "USD",
      "description": "Currency code for the amount (default USD; only sent when amountMicros is provided).",
      "type": "string"
    },
    "stage": {
      "description": "Pipeline stage — e.g. SCREENING, MEETING, PROPOSAL, NEGOTIATION, CUSTOMER (custom pipelines vary).",
      "type": "string"
    },
    "closeDate": {
      "description": "Expected close date (ISO 8601).",
      "type": "string"
    },
    "companyId": {
      "description": "Id of the associated company.",
      "type": "string"
    },
    "pointOfContactId": {
      "description": "Id of the point-of-contact person.",
      "type": "string"
    },
    "extra": {
      "description": "Passthrough object of additional/custom fields merged into the request body verbatim.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "required": [
    "name"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🔴twenty_update_opportunity(id, name, amountMicros, currencyCode, stage, ...)

Update fields on an existing opportunity. Only provided fields change. Twenty REST: PATCH /rest/opportunities/{id}.

Input Schema

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "The opportunity id to update."
    },
    "name": {
      "description": "The opportunity name.",
      "type": "string"
    },
    "amountMicros": {
      "description": "Opportunity amount in micros (× 1,000,000). Sent as amount:{amountMicros,currencyCode}.",
      "type": "number"
    },
    "currencyCode": {
      "default": "USD",
      "description": "Currency code for the amount (default USD; only sent when amountMicros is provided).",
      "type": "string"
    },
    "stage": {
      "description": "Pipeline stage — e.g. SCREENING, MEETING, PROPOSAL, NEGOTIATION, CUSTOMER (custom pipelines vary).",
      "type": "string"
    },
    "closeDate": {
      "description": "Expected close date (ISO 8601).",
      "type": "string"
    },
    "companyId": {
      "description": "Id of the associated company.",
      "type": "string"
    },
    "pointOfContactId": {
      "description": "Id of the point-of-contact person.",
      "type": "string"
    },
    "extra": {
      "description": "Passthrough object of additional/custom fields merged into the request body verbatim.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "required": [
    "id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🔴twenty_create_note(title, body, extra)

Create a note. `title` is required; `body` holds the note content. (Attaching to records via noteTargets is out of scope.) Twenty REST: POST /rest/notes.

Input Schema

{
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "description": "The note title (required)."
    },
    "body": {
      "description": "The note content/body.",
      "type": "string"
    },
    "extra": {
      "description": "Passthrough object of additional/custom fields merged into the request body verbatim.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "required": [
    "title"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🔴twenty_create_task(title, body, status, dueAt, assigneeId, ...)

Create a task. `title` is required. Twenty REST: POST /rest/tasks.

Input Schema

{
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "description": "The task title (required)."
    },
    "body": {
      "description": "Task body / description.",
      "type": "string"
    },
    "status": {
      "description": "Task status — e.g. TODO, IN_PROGRESS, DONE.",
      "type": "string"
    },
    "dueAt": {
      "description": "Due date/time (ISO 8601).",
      "type": "string"
    },
    "assigneeId": {
      "description": "Id of the workspace member assigned to the task.",
      "type": "string"
    },
    "extra": {
      "description": "Passthrough object of additional/custom fields merged into the request body verbatim.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "required": [
    "title"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}
🔴twenty_update_task(id, title, body, status, dueAt, ...)

Update fields on an existing task. Only provided fields change. Twenty REST: PATCH /rest/tasks/{id}.

Input Schema

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "The task id to update."
    },
    "title": {
      "description": "The task title.",
      "type": "string"
    },
    "body": {
      "description": "Task body / description.",
      "type": "string"
    },
    "status": {
      "description": "Task status — e.g. TODO, IN_PROGRESS, DONE.",
      "type": "string"
    },
    "dueAt": {
      "description": "Due date/time (ISO 8601).",
      "type": "string"
    },
    "assigneeId": {
      "description": "Id of the workspace member assigned to the task.",
      "type": "string"
    },
    "extra": {
      "description": "Passthrough object of additional/custom fields merged into the request body verbatim.",
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    }
  },
  "required": [
    "id"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}

Community

Rate this Server

Evidence

Recent observations

verifiedversion not recorded20 tools
verifiedversion not recorded20 tools
verifiedversion not recorded20 tools
verifiedversion not recorded20 tools
failed observationversion not recorded—