Search, create, and manage your contacts, notes, and reminders directly from Claude. Dex is a personal CRM that helps you nurture your professional relationships — and with this integration, it becomes a native part of your AI workflow. Prepare for meetings by pulling up contact history, log interactions after a call, set follow-up reminders, organize contacts with tags and groups, and clean up duplicates — all without switching apps.
Protocol
MCP 2026-07-28
Server version
3.1.0
Supports
Tools
Resources
Overall score
B
Overall quality score: 83 out of 10083/100
Server metadata
3 of 8 fields30/1002.4 / 8 pts
Identity, discovery, and usage metadata advertised during the MCP handshake.
App resources, metadata, and rendering requirements.
Tool invocation metadata present
3 tool(s) missing openai/toolInvocation metadata
How to fix: For tools that render a UI resource, add openai/toolInvocation with string fields invoking and invoked (present-tense labels shown while the tool runs and after it finishes, e.g. "Searching…" / "Searched").
How to fix: Use a ui:// URI and text/html;profile=mcp-app. For mcp-use servers, set the widget description in widgetMetadata; other stacks can use _meta.ui.widgetDescription or the OpenAI compatibility alias.
How to fix: For ChatGPT submission, set one exact HTTPS origin such as "https://app.example.com" on every view via _meta.ui.domain (or openai/widgetDomain). Paths are not valid and all views in one app must share the origin.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description83/100
Search contacts by name, email, phone number, or any keyword. Phone numbers are matched on digits; formatting and country code are ignored (a query that is only a phone number searches phone numbers instead of the text fields, so "+420 604 354 254", "420604354254" and "604354254" all find the same contact). Use `near` for geographic proximity ("contacts in the Bay Area", "near Sydney") — the place is geocoded and contacts are matched by their own coordinates, so it finds people whose saved location differs from the query (e.g. "San Francisco" still matches "Bay Area"). `near` and `query` combine (AND): "engineers in the Bay Area" → query "engineer" + near "Bay Area". Returns up to `limit` results. Use an empty query to list contacts sorted by most recently interacted.
Describe the returned information so the agent knows whether it will answer the request.
Inputs94/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
query
Search query (name, company, job title, phone number, etc.). A phone number is matched on its digits only — pass it in any formatting, with or without the country code. Use empty string to list all contacts. When `near` is set, use this ONLY for an additional non-location filter to narrow within the area (e.g. "engineer", "Google") — leave it empty for everyone nearby, and do NOT repeat the place name here (that over-filters).
Clear enough to supply this argument.
near
Geographic proximity search: a place to find contacts near ("Bay Area", "Sydney", "Austin, TX"). Matches contacts by their coordinates within `radius_km`.
Clear enough to supply this argument.
radius_km
Radius in km around `near` (default 50). Use a larger value for a region/state, smaller for a neighbourhood.
Clear enough to supply this argument.
limit
Max results to return (default 50, max 200)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"count"
],
"properties": {
"count": {
"type": "number",
"description": "Number of contacts in `items`"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"company": {
"type": "string"
},
"starred": {
"type": "boolean"
},
"frequency": {
"type": "string",
"description": "Keep-in-touch cadence, e.g. \"7 days\""
},
"full_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
},
"is_archived": {
"type": "boolean"
},
"legacy_location": {
"type": "string"
},
"never_keep_in_touch": {
"type": "boolean",
"description": "true when the contact is marked don't-keep-in-touch"
}
},
"description": "A contact. Fields whose stored value is null are omitted entirely, so treat every field as optional; use dex_get_contact for the full record.",
"additionalProperties": {}
},
"description": "Matching contacts"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_search_contacts",
"title": "Search Contacts",
"description": "Search contacts by name, email, phone number, or any keyword. Phone numbers are matched on digits; formatting and country code are ignored (a query that is only a phone number searches phone numbers instead of the text fields, so \"+420 604 354 254\", \"420604354254\" and \"604354254\" all find the same contact). Use `near` for geographic proximity (\"contacts in the Bay Area\", \"near Sydney\") — the place is geocoded and contacts are matched by their own coordinates, so it finds people whose saved location differs from the query (e.g. \"San Francisco\" still matches \"Bay Area\"). `near` and `query` combine (AND): \"engineers in the Bay Area\" → query \"engineer\" + near \"Bay Area\". Returns up to `limit` results. Use an empty query to list contacts sorted by most recently interacted.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"query"
],
"properties": {
"near": {
"type": "string",
"description": "Geographic proximity search: a place to find contacts near (\"Bay Area\", \"Sydney\", \"Austin, TX\"). Matches contacts by their coordinates within `radius_km`."
},
"limit": {
"type": "number",
"default": 50,
"description": "Max results to return (default 50, max 200)"
},
"query": {
"type": "string",
"description": "Search query (name, company, job title, phone number, etc.). A phone number is matched on its digits only — pass it in any formatting, with or without the country code. Use empty string to list all contacts. When `near` is set, use this ONLY for an additional non-location filter to narrow within the area (e.g. \"engineer\", \"Google\") — leave it empty for everyone nearby, and do NOT repeat the place name here (that over-filters)."
},
"radius_km": {
"type": "number",
"description": "Radius in km around `near` (default 50). Use a larger value for a region/state, smaller for a neighbourhood.",
"exclusiveMinimum": 0
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"count"
],
"properties": {
"count": {
"type": "number",
"description": "Number of contacts in `items`"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"company": {
"type": "string"
},
"starred": {
"type": "boolean"
},
"frequency": {
"type": "string",
"description": "Keep-in-touch cadence, e.g. \"7 days\""
},
"full_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
},
"is_archived": {
"type": "boolean"
},
"legacy_location": {
"type": "string"
},
"never_keep_in_touch": {
"type": "boolean",
"description": "true when the contact is marked don't-keep-in-touch"
}
},
"description": "A contact. Fields whose stored value is null are omitted entirely, so treat every field as optional; use dex_get_contact for the full record.",
"additionalProperties": {}
},
"description": "Matching contacts"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description95/100
List all contacts with cursor-based pagination. Returns lightweight contact summaries (id, name, company, job title, location). Use dex_get_contact for full details on a specific contact. Use dex_search_contacts for keyword search.
The description explains the capability, when to use it, and what to expect.
Inputs75/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
cursor
Pagination cursor from previous response
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
limit
Results per page (default 100, max 500)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description95/100
Get a single contact by ID with full details including emails, phone numbers, location (text plus latitude/longitude when geocoded), keep-in-touch cadence (`frequency` / `never_keep_in_touch`), tags, groups, custom fields, related contacts, and optionally recent notes/timeline. Use `fields` to request only the relation data needed and keep rich contacts within the response limit.
The description explains the capability, when to use it, and what to expect.
Inputs81/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
id
Contact ID
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
fields
Optional allowlist of top-level relation fields. Scalar contact fields are always returned.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
custom_field_ids
Return only these custom-field value rows. Use with fields: ["contacts_custom_fields"] for the smallest response.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
include_notes
Include recent notes/timeline items when fields is omitted (default false). When fields is supplied, select recent_notes there instead.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description83/100
Create one or more contacts. For a single contact, pass fields directly (first_name, email, etc.). For batch creation (e.g. CSV import), pass a `contacts` array instead. Returns the created contact(s).
Describe the returned information so the agent knows whether it will answer the request.
Inputs91/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
first_name
First name
Clear enough to supply this argument.
last_name
Last name
Clear enough to supply this argument.
company
Company name
Explain the meaning of this value, not just its name.
job_title
Job title
Explain the meaning of this value, not just its name.
email
Primary email address (shorthand for a single email)
Clear enough to supply this argument.
emails › items › email
Email address
Clear enough to supply this argument.
emails › items › label
Label (e.g. "Work", "Personal")
Clear enough to supply this argument.
phone
Primary phone number (shorthand for a single phone)
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
phones › items › phone_number
Phone number
Explain the meaning of this value, not just its name.
phones › items › label
Label (e.g. "Mobile", "Work")
Clear enough to supply this argument.
phones › items › country_code
Dial code without + prefix (e.g. "1", "52", "44")
Clear enough to supply this argument.
linkedin
LinkedIn profile URL
Clear enough to supply this argument.
twitter
Twitter/X handle
Clear enough to supply this argument.
birthday
Birthday as `YYYY-MM-DD`. When the year is unknown pass the year-less form `--MM-DD` (e.g. "--03-14"); the year is stored separately, so a full date keeps its year and a year-less one reads back as `--MM-DD`.
Clear enough to supply this argument.
description
Notes about the contact
Clear enough to supply this argument.
website
Website URL
Clear enough to supply this argument.
location
Where the contact is based, e.g. "Lisbon, Portugal". Saved as their location (read back as `legacy_location`) and geocoded by Dex — do not put it in description.
Clear enough to supply this argument.
contacts › items › first_name
First name
Clear enough to supply this argument.
contacts › items › last_name
Last name
Clear enough to supply this argument.
contacts › items › company
Company name
Clear enough to supply this argument.
contacts › items › job_title
Job title
Clear enough to supply this argument.
contacts › items › email
Primary email address (shorthand for a single email)
Clear enough to supply this argument.
contacts › items › emails › items › email
Email address
Clear enough to supply this argument.
contacts › items › emails › items › label
Label (e.g. "Work", "Personal")
Clear enough to supply this argument.
contacts › items › phone
Primary phone number (shorthand for a single phone)
Clear enough to supply this argument.
contacts › items › phones › items › phone_number
Phone number
Clear enough to supply this argument.
contacts › items › phones › items › label
Label (e.g. "Mobile", "Work")
Clear enough to supply this argument.
contacts › items › phones › items › country_code
Dial code without + prefix (e.g. "1", "52", "44")
Clear enough to supply this argument.
contacts › items › linkedin
LinkedIn profile URL
Clear enough to supply this argument.
contacts › items › twitter
Twitter/X handle
Clear enough to supply this argument.
contacts › items › birthday
Birthday as `YYYY-MM-DD`. When the year is unknown pass the year-less form `--MM-DD` (e.g. "--03-14"); the year is stored separately, so a full date keeps its year and a year-less one reads back as `--MM-DD`.
Clear enough to supply this argument.
contacts › items › description
Notes about the contact
Clear enough to supply this argument.
contacts › items › website
Website URL
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
contacts › items › location
Where the contact is based, e.g. "Lisbon, Portugal". Saved as their location (read back as `legacy_location`) and geocoded by Dex — do not put it in description.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description88/100
Update an existing contact. Only provided fields are changed. Pass `null` to clear an optional scalar such as website, company, job title, social profile, birthday, description, or location. For emails and phone numbers, you can add or remove entries — existing ones are preserved automatically. `is_archived: true` ARCHIVES the contact (hidden from lists, search, and reminders; reversible) — confirm with the user first, and use dex_archive_contacts for more than one contact.
The description explains the capability, when to use it, and what to expect.
Inputs96/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
id
Contact ID to update
Clear enough to supply this argument.
first_name
First name
Clear enough to supply this argument.
last_name
Last name
Clear enough to supply this argument.
company
Company name; pass `null` to clear
Clear enough to supply this argument.
job_title
Job title; pass `null` to clear
Clear enough to supply this argument.
email
Add a single email address (shorthand for add_emails)
Clear enough to supply this argument.
add_emails › items › email
Email address
Clear enough to supply this argument.
add_emails › items › label
Label (e.g. "Work", "Personal")
Clear enough to supply this argument.
remove_emails
Email addresses to remove from this contact
Clear enough to supply this argument.
phone
Add a single phone number (shorthand for add_phones)
Clear enough to supply this argument.
add_phones › items › phone_number
Phone number
Clear enough to supply this argument.
add_phones › items › label
Label (e.g. "Mobile", "Work")
Clear enough to supply this argument.
add_phones › items › country_code
Dial code without + prefix (e.g. "1", "52", "44")
Clear enough to supply this argument.
remove_phones
Phone numbers to remove from this contact
Clear enough to supply this argument.
linkedin
LinkedIn profile URL; pass `null` to clear
Clear enough to supply this argument.
twitter
Twitter/X handle; pass `null` to clear
Clear enough to supply this argument.
birthday
Birthday as `YYYY-MM-DD`, or the year-less form `--MM-DD` when only the month and day are known (e.g. "--03-14"); pass `null` to clear it. The year is stored separately, so switching a dated birthday to `--MM-DD` clears the year while keeping the day.
Clear enough to supply this argument.
description
Notes about the contact; pass `null` to clear
Clear enough to supply this argument.
website
Website URL; pass `null` to clear
Clear enough to supply this argument.
location
Where the contact is based, e.g. "Lisbon, Portugal". Saved as their location (read back as `legacy_location`) and geocoded by Dex; pass `null` to clear
Clear enough to supply this argument.
starred
Star/unstar contact
Clear enough to supply this argument.
keep_in_touch
Keep-in-touch cadence. An interval sets how often to stay in touch ("7 days" = weekly, "14 days" = every 2 weeks, "1 mon" = monthly, "42 days" = every 6 weeks, "3 mons" = quarterly, "6 mons" = every 6 months, "1 year" = yearly) — this is "remind me to reach out every N", "stay in touch every N". "never" is the explicit don't-keep-in-touch mark — "no regular outreach", "stop reaching out", "don't keep in touch with them", "stop reminding me about them". "unset" simply clears the cadence so the contact has none — "no cadence", "remove the keep-in-touch" — leaving them unmarked rather than flagged. To apply one cadence across many contacts at once, use dex_set_keep_in_touch instead.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
is_archived
true archives this one contact (hidden from lists, search, and reminders; reversible), false restores it. Confirm before archiving; for several contacts use dex_archive_contacts.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description94/100
Mark a keep-in-touch contact as done — records that the user was just in touch, so the cadence resets and the next reminder lands one frequency interval from now (the dashboard's "Mark as complete"). Pass snooze_days to instead push the next reminder out without recording a touch (the dashboard's snooze uses 7). Set or clear the cadence itself via dex_update_contact's keep_in_touch field, or dex_set_keep_in_touch for many contacts at once.
The description explains the capability, when to use it, and what to expect.
Inputs67/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
contact_id
Contact ID
Explain the meaning of this value, not just its name.
snooze_days
Snooze instead of completing: push the next keep-in-touch reminder this many days from now
Explain what happens when this optional value is omitted.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"contact": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"company": {
"type": "string"
},
"starred": {
"type": "boolean"
},
"frequency": {
"type": "string",
"description": "Keep-in-touch cadence, e.g. \"7 days\""
},
"full_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
},
"is_archived": {
"type": "boolean"
},
"legacy_location": {
"type": "string"
},
"never_keep_in_touch": {
"type": "boolean",
"description": "true when the contact is marked don't-keep-in-touch"
}
},
"description": "The contact after the keep-in-touch update",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_complete_keep_in_touch",
"title": "Complete Keep-in-Touch",
"description": "Mark a keep-in-touch contact as done — records that the user was just in touch, so the cadence resets and the next reminder lands one frequency interval from now (the dashboard's \"Mark as complete\"). Pass snooze_days to instead push the next reminder out without recording a touch (the dashboard's snooze uses 7). Set or clear the cadence itself via dex_update_contact's keep_in_touch field, or dex_set_keep_in_touch for many contacts at once.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"contact_id"
],
"properties": {
"contact_id": {
"type": "string",
"description": "Contact ID"
},
"snooze_days": {
"anyOf": [
{
"type": "integer",
"maximum": 9007199254740991,
"exclusiveMinimum": 0
},
{
"type": "null"
}
],
"description": "Snooze instead of completing: push the next keep-in-touch reminder this many days from now"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"contact": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"company": {
"type": "string"
},
"starred": {
"type": "boolean"
},
"frequency": {
"type": "string",
"description": "Keep-in-touch cadence, e.g. \"7 days\""
},
"full_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
},
"is_archived": {
"type": "boolean"
},
"legacy_location": {
"type": "string"
},
"never_keep_in_touch": {
"type": "boolean",
"description": "true when the contact is marked don't-keep-in-touch"
}
},
"description": "The contact after the keep-in-touch update",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description90/100
Set or clear the keep-in-touch cadence for up to 500 contacts in one call (chunk larger sets into multiple calls) — the bulk companion to dex_update_contact's keep_in_touch field. An interval sets how often to stay in touch ("7 days" = weekly, "14 days" = every 2 weeks, "1 mon" = monthly, "42 days" = every 6 weeks, "3 mons" = quarterly, "6 mons" = every 6 months, "1 year" = yearly) — this is "remind me to reach out every N", "stay in touch every N". "never" is the explicit don't-keep-in-touch mark — "no regular outreach", "stop reaching out", "don't keep in touch with them", "stop reminding me about them". "unset" simply clears the cadence so the contact has none — "no cadence", "remove the keep-in-touch" — leaving them unmarked rather than flagged. Every value overwrites whatever cadence each contact already has — and "never"/"unset" leave nothing in its place — so confirm with the user before changing a large set. Marking a due keep-in-touch as done is dex_complete_keep_in_touch, not this tool.
The description explains the capability, when to use it, and what to expect.
Inputs75/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
contact_ids
Contact IDs to put on the cadence (max 500 per call)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
cadence
Applied to ALL of contact_ids: a reminder interval, "never" (don't keep in touch), or "unset" (no cadence).
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"cadence",
"requested",
"updated"
],
"properties": {
"cadence": {
"type": "string",
"description": "The cadence that was requested"
},
"updated": {
"type": "number",
"description": "IDs verified to carry the requested cadence afterwards"
},
"requested": {
"type": "number",
"description": "Distinct contact IDs requested (duplicates collapsed)"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"not_updated_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Omitted when everything succeeded. An ID here does not carry the requested cadence — it may not exist, may belong to another user, or its write may have failed."
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_set_keep_in_touch",
"title": "Set Keep-in-Touch Cadence",
"description": "Set or clear the keep-in-touch cadence for up to 500 contacts in one call (chunk larger sets into multiple calls) — the bulk companion to dex_update_contact's keep_in_touch field. An interval sets how often to stay in touch (\"7 days\" = weekly, \"14 days\" = every 2 weeks, \"1 mon\" = monthly, \"42 days\" = every 6 weeks, \"3 mons\" = quarterly, \"6 mons\" = every 6 months, \"1 year\" = yearly) — this is \"remind me to reach out every N\", \"stay in touch every N\". \"never\" is the explicit don't-keep-in-touch mark — \"no regular outreach\", \"stop reaching out\", \"don't keep in touch with them\", \"stop reminding me about them\". \"unset\" simply clears the cadence so the contact has none — \"no cadence\", \"remove the keep-in-touch\" — leaving them unmarked rather than flagged. Every value overwrites whatever cadence each contact already has — and \"never\"/\"unset\" leave nothing in its place — so confirm with the user before changing a large set. Marking a due keep-in-touch as done is dex_complete_keep_in_touch, not this tool.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"contact_ids",
"cadence"
],
"properties": {
"cadence": {
"enum": [
"7 days",
"14 days",
"1 mon",
"42 days",
"3 mons",
"6 mons",
"1 year",
"never",
"unset"
],
"type": "string",
"description": "Applied to ALL of contact_ids: a reminder interval, \"never\" (don't keep in touch), or \"unset\" (no cadence)."
},
"contact_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"maxItems": 500,
"minItems": 1,
"description": "Contact IDs to put on the cadence (max 500 per call)"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"cadence",
"requested",
"updated"
],
"properties": {
"cadence": {
"type": "string",
"description": "The cadence that was requested"
},
"updated": {
"type": "number",
"description": "IDs verified to carry the requested cadence afterwards"
},
"requested": {
"type": "number",
"description": "Distinct contact IDs requested (duplicates collapsed)"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"not_updated_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Omitted when everything succeeded. An ID here does not carry the requested cadence — it may not exist, may belong to another user, or its write may have failed."
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description96/100
Archive or unarchive up to 500 contacts by ID (chunk larger sets into multiple calls). Archived contacts keep all their data (notes, reminders, history) but are hidden from the default contact list, search, and keep-in-touch reminders — the reversible alternative to dex_delete_contacts, recommended for bulk cleanup. Always show the user which contacts will be archived and get their confirmation first. Archiving MORE THAN 20 contacts in one call is server-enforced: the call is rejected (nothing changes) unless it carries confirm: true AND expected_count equal to the number of distinct contact_ids — the count the user confirmed. Restoring (archived: false) is never gated. Find archived contacts with dex_filter_contacts archived_only:true and restore them with archived:false.
The description explains the capability, when to use it, and what to expect.
Inputs85/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
contact_ids
Contact IDs to archive or unarchive (max 500 per call)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"archived",
"requested",
"updated"
],
"properties": {
"updated": {
"type": "number",
"description": "IDs verified to be in the requested state afterwards"
},
"archived": {
"type": "boolean",
"description": "The state that was requested: true archived, false restored"
},
"requested": {
"type": "number",
"description": "Distinct contact IDs requested (duplicates collapsed)"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"not_updated_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Omitted when everything succeeded. An ID here is not in the requested state — it may not exist, may belong to another user, or its write may have failed."
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_archive_contacts",
"title": "Archive Contacts",
"description": "Archive or unarchive up to 500 contacts by ID (chunk larger sets into multiple calls). Archived contacts keep all their data (notes, reminders, history) but are hidden from the default contact list, search, and keep-in-touch reminders — the reversible alternative to dex_delete_contacts, recommended for bulk cleanup. Always show the user which contacts will be archived and get their confirmation first. Archiving MORE THAN 20 contacts in one call is server-enforced: the call is rejected (nothing changes) unless it carries confirm: true AND expected_count equal to the number of distinct contact_ids — the count the user confirmed. Restoring (archived: false) is never gated. Find archived contacts with dex_filter_contacts archived_only:true and restore them with archived:false.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"contact_ids"
],
"properties": {
"confirm": {
"type": "boolean",
"description": "Required (true) when archiving more than 20 contacts: asserts the user saw the list or exact count and explicitly confirmed the archive"
},
"archived": {
"type": "boolean",
"default": true,
"description": "true (default) archives; false restores/unarchives"
},
"contact_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"maxItems": 500,
"minItems": 1,
"description": "Contact IDs to archive or unarchive (max 500 per call)"
},
"expected_count": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "Required when archiving more than 20 contacts: the number of distinct contact_ids in this call, as confirmed by the user"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"archived",
"requested",
"updated"
],
"properties": {
"updated": {
"type": "number",
"description": "IDs verified to be in the requested state afterwards"
},
"archived": {
"type": "boolean",
"description": "The state that was requested: true archived, false restored"
},
"requested": {
"type": "number",
"description": "Distinct contact IDs requested (duplicates collapsed)"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"not_updated_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Omitted when everything succeeded. An ID here is not in the requested state — it may not exist, may belong to another user, or its write may have failed."
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
Expand internal abbreviations and remove technical prefixes from the display title.
Description86/100
Delete one or more contacts by their IDs. This action is irreversible — when the user might want the contacts back later, use dex_archive_contacts instead.
The description explains the capability, when to use it, and what to expect.
Inputs63/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
contact_ids
Contact IDs to delete
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
Align the output schema with the tool’s documented result.
View output schema
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"count": {
"type": "number",
"description": "Batch branch: how many contacts were deleted"
},
"deleted": {
"type": "array",
"items": {
"type": "string"
},
"description": "Single-delete branch: the contact ID that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"contact_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Batch branch: the contact IDs that were deleted"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_delete_contacts",
"title": "Delete Contacts",
"description": "Delete one or more contacts by their IDs. This action is irreversible — when the user might want the contacts back later, use dex_archive_contacts instead.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"contact_ids"
],
"properties": {
"contact_ids": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1,
"description": "Contact IDs to delete"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"count": {
"type": "number",
"description": "Batch branch: how many contacts were deleted"
},
"deleted": {
"type": "array",
"items": {
"type": "string"
},
"description": "Single-delete branch: the contact ID that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"contact_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Batch branch: the contact IDs that were deleted"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
Expand internal abbreviations and remove technical prefixes from the display title.
Description98/100
Merge duplicate contacts. Each group of IDs is consolidated into one surviving contact — all data is combined onto it and the other IDs are deleted. The survivor defaults to the oldest (longest-lived) record in the group, NOT the first ID you pass; to choose the survivor yourself, pass the group as { contact_ids, primary_contact_id } instead of a plain array. Read the returned mergedContactIds to learn which contact ID survived each group instead of assuming the order you sent. This is irreversible.
The description explains the capability, when to use it, and what to expect.
Inputs83/100
Explain what happens when this optional value is omitted.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Explain what happens when this optional value is omitted.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"mergedContactIds",
"failedCount"
],
"properties": {
"failed": {
"type": "array",
"items": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
}
},
"additionalProperties": {}
},
"contactIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "The group that could not be merged"
}
},
"additionalProperties": {}
},
"description": "Omitted entirely when every group merged successfully"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"failedCount": {
"type": "number",
"description": "How many groups could not be merged. 0 on full success."
},
"mergedContactIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "One surviving contact ID per successfully merged group — the OLDEST record of each group (or the primary_contact_id you chose), NOT the first ID you passed. Read this rather than assuming the input order."
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_merge_contacts",
"title": "Merge Contacts",
"description": "Merge duplicate contacts. Each group of IDs is consolidated into one surviving contact — all data is combined onto it and the other IDs are deleted. The survivor defaults to the oldest (longest-lived) record in the group, NOT the first ID you pass; to choose the survivor yourself, pass the group as { contact_ids, primary_contact_id } instead of a plain array. Read the returned mergedContactIds to learn which contact ID survived each group instead of assuming the order you sent. This is irreversible.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"contact_id_groups"
],
"properties": {
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
},
"contact_id_groups": {
"type": "array",
"items": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 2,
"description": "Contact IDs to merge; the oldest record survives"
},
{
"type": "object",
"required": [
"contact_ids"
],
"properties": {
"contact_ids": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 2,
"description": "Contact IDs to merge into one"
},
"primary_contact_id": {
"type": "string",
"description": "Which of contact_ids survives the merge. Omit to keep the oldest record."
}
}
}
]
},
"minItems": 1,
"description": "Groups of contact IDs to merge. Each group is either a plain array of IDs (oldest record survives) or { contact_ids, primary_contact_id } to choose the survivor."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"mergedContactIds",
"failedCount"
],
"properties": {
"failed": {
"type": "array",
"items": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
}
},
"additionalProperties": {}
},
"contactIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "The group that could not be merged"
}
},
"additionalProperties": {}
},
"description": "Omitted entirely when every group merged successfully"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"failedCount": {
"type": "number",
"description": "How many groups could not be merged. 0 on full success."
},
"mergedContactIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "One surviving contact ID per successfully merged group — the OLDEST record of each group (or the primary_contact_id you chose), NOT the first ID you passed. Read this rather than assuming the input order."
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description88/100
Filter contacts with the same structured filters as Dex views: tags, groups, company, job title, social profile presence, LinkedIn history, location, interaction dates, keep-in-touch cadence, custom fields, and more. All conditions combine with AND. Up to 100 contact IDs can be fetched in one call; tenant ownership is enforced server-side. Archived contacts are omitted unless `archived_only: true`. Use this to answer questions like "contacts tagged investor at Google I talked to this year". For plain keyword or geographic search use dex_search_contacts; for full details on one contact use dex_get_contact.
The description explains the capability, when to use it, and what to expect.
Inputs89/100
39 of 40 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
ids
Fetch only these contact IDs (1–100). An absent ID may be not found, not owned, or archived. If IDs may mix active and archived contacts, run two calls: one with the default and one with archived_only: true.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
include_custom_fields
Include each contact's custom-field values in the result (default false).
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
tags
Tag IDs or exact tag names the contact must have (ANY of them by default; see tags_match)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
tags_match
Whether contacts need ANY (default) or ALL of the tags
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
exclude_tags
Tag IDs or exact names the contact must NOT have
Clear enough to supply this argument.
groups
Group IDs or exact group names the contact must be in (ANY by default; see groups_match)
Clear enough to supply this argument.
groups_match
Whether contacts need ANY (default) or ALL of the groups
Clear enough to supply this argument.
exclude_groups
Group IDs or exact names the contact must NOT be in
Clear enough to supply this argument.
name
Name contains (fuzzy)
Clear enough to supply this argument.
company
Company contains (fuzzy)
Clear enough to supply this argument.
job_title
Job title contains (fuzzy)
Clear enough to supply this argument.
education
Education contains (fuzzy)
Clear enough to supply this argument.
description_contains
Contact description/notes field contains
Clear enough to supply this argument.
location
Location text contains (fuzzy)
Clear enough to supply this argument.
near
Geographic proximity: a place to filter contacts near ("Bay Area", "Sydney", "Austin, TX"). Geocoded server-side and matched against each contact's own coordinates, so it finds people whose saved location text differs from the query. Unlike `location`, this is a real radius search, not a text match.
Clear enough to supply this argument.
radius_km
Radius in km around `near` (default 50). Larger for a region/state, smaller for a neighbourhood. Requires `near`.
Clear enough to supply this argument.
has_linkedin
Contact has (true) or lacks (false) a LinkedIn profile
Clear enough to supply this argument.
linkedin_company
LinkedIn work history contains this company
Clear enough to supply this argument.
linkedin_education
LinkedIn education history contains this school
Clear enough to supply this argument.
has_twitter
Has a Twitter/X handle
Clear enough to supply this argument.
has_instagram
Has an Instagram handle
Clear enough to supply this argument.
has_facebook
Has a Facebook profile
Clear enough to supply this argument.
has_telegram
Has a Telegram handle
Clear enough to supply this argument.
has_tiktok
Has a TikTok handle
Clear enough to supply this argument.
has_youtube
Has a YouTube channel
Clear enough to supply this argument.
has_email
Has at least one email
Clear enough to supply this argument.
has_phone
Has at least one phone number
Clear enough to supply this argument.
starred
Only starred (true) or unstarred (false) contacts
Clear enough to supply this argument.
has_frequency
Keep-in-touch cadence. Use the BOOLEAN for presence: true → contacts with ANY cadence set (this is "everyone who has one"), false → contacts with none. A cadence string narrows to ONE lane: "never" (marked don't-keep-in-touch) or an exact interval ("7 days", "14 days", "1 mon", "42 days", "3 mons", "6 mons", "1 year"). Note "unset" is a dex_set_keep_in_touch WRITE value, not a filter — to find the contacts you would unset, pass true. This is the only way to select an audience BY its cadence, so use it to build the contact_ids for a bulk dex_set_keep_in_touch.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
archived_only
true → return ONLY archived contacts (the backend cannot mix archived and active in one query; run two queries for both). Use this to find contacts to restore via dex_archive_contacts. Default: only active contacts.
Clear enough to supply this argument.
include_archived
Deprecated alias of archived_only.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
last_interaction_after
Last interaction (email/meeting) on or after this time
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
last_interaction_before
Last interaction on or before this time
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
created_after
Contact created on or after this time
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
created_before
Contact created on or before this time
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
custom_fields › items › field_id
Custom field ID from dex_list_custom_fields
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
custom_fields › items › operator
contains/eq take a string value; in/not_in take a string array; present/absent take no value; gte/lte take an ISO datetime (date fields)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
custom_fields › items › value
Not provided
Missing description. Explain what this value means and how to supply it.
limit
Max results per page (default 50, max 200)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
cursor
Pagination cursor (contact ID) from previous response
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
Describe the returned fields so an agent can interpret the result.
View output schema
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"count",
"has_more"
],
"properties": {
"count": {
"type": "number",
"description": "Total matches reported by the backend, falling back to the number of rows on this page"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"company": {
"type": "string"
},
"starred": {
"type": "boolean"
},
"full_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
},
"is_archived": {
"type": "boolean"
},
"legacy_location": {
"type": "string"
},
"contacts_custom_fields": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date_value": {
"type": "string"
},
"text_value": {
"type": "string"
},
"array_value": {
"type": "array",
"items": {
"type": "string"
}
},
"custom_field_id": {
"type": "string"
}
},
"additionalProperties": {}
},
"description": "Custom-field values, when include_custom_fields is true"
}
},
"additionalProperties": {}
},
"description": "The rows for this page"
},
"has_more": {
"type": "boolean",
"description": "Whether another page is available"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"next_cursor": {
"type": "string",
"description": "Pass as `cursor` to fetch the next page"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_filter_contacts",
"title": "Filter Contacts",
"description": "Filter contacts with the same structured filters as Dex views: tags, groups, company, job title, social profile presence, LinkedIn history, location, interaction dates, keep-in-touch cadence, custom fields, and more. All conditions combine with AND. Up to 100 contact IDs can be fetched in one call; tenant ownership is enforced server-side. Archived contacts are omitted unless `archived_only: true`. Use this to answer questions like \"contacts tagged investor at Google I talked to this year\". For plain keyword or geographic search use dex_search_contacts; for full details on one contact use dex_get_contact.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"maxItems": 100,
"minItems": 1,
"description": "Fetch only these contact IDs (1–100). An absent ID may be not found, not owned, or archived. If IDs may mix active and archived contacts, run two calls: one with the default and one with archived_only: true."
},
"name": {
"type": "string",
"description": "Name contains (fuzzy)"
},
"near": {
"type": "string",
"description": "Geographic proximity: a place to filter contacts near (\"Bay Area\", \"Sydney\", \"Austin, TX\"). Geocoded server-side and matched against each contact's own coordinates, so it finds people whose saved location text differs from the query. Unlike `location`, this is a real radius search, not a text match."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Tag IDs or exact tag names the contact must have (ANY of them by default; see tags_match)"
},
"limit": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Max results per page (default 50, max 200)"
},
"cursor": {
"type": "string",
"description": "Pagination cursor (contact ID) from previous response"
},
"groups": {
"type": "array",
"items": {
"type": "string"
},
"description": "Group IDs or exact group names the contact must be in (ANY by default; see groups_match)"
},
"company": {
"type": "string",
"description": "Company contains (fuzzy)"
},
"starred": {
"type": "boolean",
"description": "Only starred (true) or unstarred (false) contacts"
},
"location": {
"type": "string",
"description": "Location text contains (fuzzy)"
},
"education": {
"type": "string",
"description": "Education contains (fuzzy)"
},
"has_email": {
"type": "boolean",
"description": "Has at least one email"
},
"has_phone": {
"type": "boolean",
"description": "Has at least one phone number"
},
"job_title": {
"type": "string",
"description": "Job title contains (fuzzy)"
},
"radius_km": {
"type": "number",
"description": "Radius in km around `near` (default 50). Larger for a region/state, smaller for a neighbourhood. Requires `near`.",
"exclusiveMinimum": 0
},
"has_tiktok": {
"type": "boolean",
"description": "Has a TikTok handle"
},
"tags_match": {
"enum": [
"any",
"all"
],
"type": "string",
"description": "Whether contacts need ANY (default) or ALL of the tags"
},
"has_twitter": {
"type": "boolean",
"description": "Has a Twitter/X handle"
},
"has_youtube": {
"type": "boolean",
"description": "Has a YouTube channel"
},
"exclude_tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Tag IDs or exact names the contact must NOT have"
},
"groups_match": {
"enum": [
"any",
"all"
],
"type": "string",
"description": "Whether contacts need ANY (default) or ALL of the groups"
},
"has_facebook": {
"type": "boolean",
"description": "Has a Facebook profile"
},
"has_linkedin": {
"type": "boolean",
"description": "Contact has (true) or lacks (false) a LinkedIn profile"
},
"has_telegram": {
"type": "boolean",
"description": "Has a Telegram handle"
},
"archived_only": {
"type": "boolean",
"description": "true → return ONLY archived contacts (the backend cannot mix archived and active in one query; run two queries for both). Use this to find contacts to restore via dex_archive_contacts. Default: only active contacts."
},
"created_after": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "Contact created on or after this time"
},
"custom_fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"field_id",
"operator"
],
"properties": {
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"field_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Custom field ID from dex_list_custom_fields"
},
"operator": {
"enum": [
"contains",
"eq",
"in",
"not_in",
"present",
"absent",
"gte",
"lte"
],
"type": "string",
"description": "contains/eq take a string value; in/not_in take a string array; present/absent take no value; gte/lte take an ISO datetime (date fields)"
}
}
},
"description": "Filters on user-defined custom fields (AND semantics)"
},
"has_frequency": {
"anyOf": [
{
"type": "boolean"
},
{
"enum": [
"7 days",
"14 days",
"1 mon",
"42 days",
"3 mons",
"6 mons",
"1 year",
"never"
],
"type": "string"
}
],
"description": "Keep-in-touch cadence. Use the BOOLEAN for presence: true → contacts with ANY cadence set (this is \"everyone who has one\"), false → contacts with none. A cadence string narrows to ONE lane: \"never\" (marked don't-keep-in-touch) or an exact interval (\"7 days\", \"14 days\", \"1 mon\", \"42 days\", \"3 mons\", \"6 mons\", \"1 year\"). Note \"unset\" is a dex_set_keep_in_touch WRITE value, not a filter — to find the contacts you would unset, pass true. This is the only way to select an audience BY its cadence, so use it to build the contact_ids for a bulk dex_set_keep_in_touch."
},
"has_instagram": {
"type": "boolean",
"description": "Has an Instagram handle"
},
"created_before": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "Contact created on or before this time"
},
"exclude_groups": {
"type": "array",
"items": {
"type": "string"
},
"description": "Group IDs or exact names the contact must NOT be in"
},
"include_archived": {
"type": "boolean",
"description": "Deprecated alias of archived_only."
},
"linkedin_company": {
"type": "string",
"description": "LinkedIn work history contains this company"
},
"linkedin_education": {
"type": "string",
"description": "LinkedIn education history contains this school"
},
"description_contains": {
"type": "string",
"description": "Contact description/notes field contains"
},
"include_custom_fields": {
"type": "boolean",
"description": "Include each contact's custom-field values in the result (default false)."
},
"last_interaction_after": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "Last interaction (email/meeting) on or after this time"
},
"last_interaction_before": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "Last interaction on or before this time"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"count",
"has_more"
],
"properties": {
"count": {
"type": "number",
"description": "Total matches reported by the backend, falling back to the number of rows on this page"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"company": {
"type": "string"
},
"starred": {
"type": "boolean"
},
"full_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"last_name": {
"type": "string"
},
"first_name": {
"type": "string"
},
"is_archived": {
"type": "boolean"
},
"legacy_location": {
"type": "string"
},
"contacts_custom_fields": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date_value": {
"type": "string"
},
"text_value": {
"type": "string"
},
"array_value": {
"type": "array",
"items": {
"type": "string"
}
},
"custom_field_id": {
"type": "string"
}
},
"additionalProperties": {}
},
"description": "Custom-field values, when include_custom_fields is true"
}
},
"additionalProperties": {}
},
"description": "The rows for this page"
},
"has_more": {
"type": "boolean",
"description": "Whether another page is available"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"next_cursor": {
"type": "string",
"description": "Pass as `cursor` to fetch the next page"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description92/100
Link one or more contacts to a contact as related contacts (e.g. family members, colleagues, people met through them). Relations are symmetric — linking A to B also links B to A — and carry no relationship label, so record the nature of the relationship in a note or the description instead. Resolve names to IDs with dex_search_contacts first. Related contacts appear in dex_get_contact under `related_contacts`. Re-linking an existing pair is a no-op.
The description explains the capability, when to use it, and what to expect.
Inputs92/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"added"
],
"properties": {
"added": {
"type": "number",
"description": "Relation links created (already-linked pairs skipped)"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_add_related_contacts",
"title": "Add Related Contacts",
"description": "Link one or more contacts to a contact as related contacts (e.g. family members, colleagues, people met through them). Relations are symmetric — linking A to B also links B to A — and carry no relationship label, so record the nature of the relationship in a note or the description instead. Resolve names to IDs with dex_search_contacts first. Related contacts appear in dex_get_contact under `related_contacts`. Re-linking an existing pair is a no-op.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"contact_id",
"contact_ids"
],
"properties": {
"contact_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The contact to link relations onto"
},
"contact_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"minItems": 1,
"description": "Contact IDs to relate to `contact_id`"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"added"
],
"properties": {
"added": {
"type": "number",
"description": "Relation links created (already-linked pairs skipped)"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description87/100
Unlink one or more related contacts from a contact. Relations are symmetric, so removing A↔B clears the link from both sides. The contacts themselves are not modified or deleted.
Explain mutations, irreversible effects, or prerequisites that matter before calling this tool.
Inputs92/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"removed"
],
"properties": {
"removed": {
"type": "number",
"description": "Relation rows removed. Legacy data may have stored a pair in both directions, so this can exceed the number of pairs requested; 0 means no link existed."
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_remove_related_contacts",
"title": "Remove Related Contacts",
"description": "Unlink one or more related contacts from a contact. Relations are symmetric, so removing A↔B clears the link from both sides. The contacts themselves are not modified or deleted.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"contact_id",
"contact_ids"
],
"properties": {
"contact_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The contact to unlink relations from"
},
"contact_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"minItems": 1,
"description": "Contact IDs to unlink from `contact_id`"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"removed"
],
"properties": {
"removed": {
"type": "number",
"description": "Relation rows removed. Legacy data may have stored a pair in both directions, so this can exceed the number of pairs requested; 0 means no link existed."
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
Expand internal abbreviations and remove technical prefixes from the display title.
Description92/100
List contact groups, most recent page first. Returns up to `limit` groups; if `has_more` is true, pass `next_cursor` back as `cursor` to fetch the rest.
The description explains the capability, when to use it, and what to expect.
Inputs75/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
cursor
Pagination cursor from previous response
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
limit
Results per page (default 200)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description82/100
Update an existing group: name, emoji, description, or parent group. To nest a group under another, pass parent_id; pass parent_id: null to move it to the top level. A group cannot be its own parent or ancestor. Because null fields are omitted from tool results, a top-level group reads back with no parent_id key.
Describe the returned information so the agent knows whether it will answer the request.
Inputs67/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
group_id
Group ID
Explain the meaning of this value, not just its name.
name
New group name
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
emoji
New emoji icon
Explain the meaning of this value, not just its name.
description
New description
Explain the meaning of this value, not just its name.
parent_id
Parent group ID to nest this group under; null moves it to the top level; omit to leave unchanged. A group cannot be its own parent or ancestor.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"group": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Group ID"
},
"name": {
"type": "string",
"description": "Group name"
},
"emoji": {
"type": "string",
"description": "Emoji icon shown next to the group"
},
"ranking": {
"type": "number",
"description": "Manual sort position"
},
"parent_id": {
"type": "string",
"description": "Parent group ID for nested groups"
},
"description": {
"type": "string",
"description": "Free-text group description"
}
},
"description": "The group, when the API returned one",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_update_group",
"title": "Update Group",
"description": "Update an existing group: name, emoji, description, or parent group. To nest a group under another, pass parent_id; pass parent_id: null to move it to the top level. A group cannot be its own parent or ancestor. Because null fields are omitted from tool results, a top-level group reads back with no parent_id key.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"group_id"
],
"properties": {
"name": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "New group name"
},
"emoji": {
"type": "string",
"description": "New emoji icon"
},
"group_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Group ID"
},
"parent_id": {
"anyOf": [
{
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
{
"type": "null"
}
],
"description": "Parent group ID to nest this group under; null moves it to the top level; omit to leave unchanged. A group cannot be its own parent or ancestor."
},
"description": {
"type": "string",
"description": "New description"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"group": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Group ID"
},
"name": {
"type": "string",
"description": "Group name"
},
"emoji": {
"type": "string",
"description": "Emoji icon shown next to the group"
},
"ranking": {
"type": "number",
"description": "Manual sort position"
},
"parent_id": {
"type": "string",
"description": "Parent group ID for nested groups"
},
"description": {
"type": "string",
"description": "Free-text group description"
}
},
"description": "The group, when the API returned one",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_delete_group",
"title": "Delete Group",
"description": "Delete a group. This action is irreversible.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"group_id"
],
"properties": {
"group_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Group ID to delete"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"removed"
],
"properties": {
"count": {
"type": "number",
"description": "Number of memberships deleted"
},
"removed": {
"type": "array",
"items": {
"type": "string"
},
"description": "Contact IDs the API processed for removal, including already absent memberships"
},
"remaining": {
"type": "array",
"items": {
"type": "string"
},
"description": "Contact IDs still to process; retry these IDs to finish the write"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_remove_contacts_from_group",
"title": "Remove Contacts from Group",
"description": "Remove up to 500 contacts from a group per call. Split larger selections into separate calls. Retry any IDs in remaining to finish a partial write.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"group_id",
"contact_ids"
],
"properties": {
"group_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Group ID"
},
"contact_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"maxItems": 500,
"minItems": 1,
"description": "Contact IDs to remove"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"removed"
],
"properties": {
"count": {
"type": "number",
"description": "Number of memberships deleted"
},
"removed": {
"type": "array",
"items": {
"type": "string"
},
"description": "Contact IDs the API processed for removal, including already absent memberships"
},
"remaining": {
"type": "array",
"items": {
"type": "string"
},
"description": "Contact IDs still to process; retry these IDs to finish the write"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
Describe the returned fields so an agent can interpret the result.
View output schema
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"tag": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Tag ID"
},
"name": {
"type": "string",
"description": "Tag name"
}
},
"description": "The tag, when the API returned one",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_get_tag",
"title": "Get Tag",
"description": "Get a single tag by ID.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"tag_id"
],
"properties": {
"tag_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Tag ID"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"tag": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Tag ID"
},
"name": {
"type": "string",
"description": "Tag name"
}
},
"description": "The tag, when the API returned one",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"tag": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Tag ID"
},
"name": {
"type": "string",
"description": "Tag name"
}
},
"description": "The tag, when the API returned one",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_create_tag",
"title": "Create Tag",
"description": "Create a new tag.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "Tag name"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"tag": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Tag ID"
},
"name": {
"type": "string",
"description": "Tag name"
}
},
"description": "The tag, when the API returned one",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": false
}
}
Describe the returned fields so an agent can interpret the result.
View output schema
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"tag": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Tag ID"
},
"name": {
"type": "string",
"description": "Tag name"
}
},
"description": "The tag, when the API returned one",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_update_tag",
"title": "Update Tag",
"description": "Update an existing tag.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"tag_id"
],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "New tag name"
},
"tag_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Tag ID"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"tag": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Tag ID"
},
"name": {
"type": "string",
"description": "Tag name"
}
},
"description": "The tag, when the API returned one",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_delete_tag",
"title": "Delete Tag",
"description": "Delete a tag. This action is irreversible.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"tag_id"
],
"properties": {
"tag_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Tag ID to delete"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description75/100
List reminders/tasks with optional filtering by contact, completion status, overdue status and due-date range, plus sorting and pagination.
The description explains the capability, when to use it, and what to expect.
Inputs92/100
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
contact_id
Filter by contact ID
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
is_complete
Filter by completion status: false for open reminders, true for completed. Omit for all.
Clear enough to supply this argument.
is_overdue
Filter by due date: true for reminders already past due, false for those due today or later. Combine with is_complete=false for overdue open reminders.
Clear enough to supply this argument.
due_after
Only reminders due on or after this date, inclusive (YYYY-MM-DD)
Clear enough to supply this argument.
due_before
Only reminders due on or before this date, inclusive (YYYY-MM-DD)
Clear enough to supply this argument.
sort_by
Field to sort by (default due_at_date)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
sort_order
Sort direction (default asc). Pair with sort_by=due_at_date and sort_order=desc to read the most recent reminders first.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description87/100
Create a new reminder/task. When `idempotency_key` is supplied, repeat calls with the same key within the server's 24-hour idempotency window return the original reminder instead of creating a duplicate.
The description explains the capability, when to use it, and what to expect.
Inputs75/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
due_at_date
Due date in YYYY-MM-DD format (e.g. "2026-03-01")
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
text
Reminder text content. Use this for the reminder title or description — there is no separate title field.
Clear enough to supply this argument.
contact_id
Associated contact ID
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
recurrence
Recurrence frequency
Explain the meaning of this value, not just its name.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical reminder creation.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_delete_reminder",
"title": "Delete Reminder",
"description": "Delete a reminder. This action is irreversible.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"reminder_id"
],
"properties": {
"reminder_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Reminder ID to delete"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description72/100
List notes on contact timelines with optional filtering by contact and date range.
Describe the returned information so the agent knows whether it will answer the request.
Inputs88/100
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
contact_id
Filter by contact ID
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
cursor
Pagination cursor from previous response
Clear enough to supply this argument.
limit
Results per page (default 10)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
include_contacts
Include the contacts this note is linked to. Off by default — note rows omit `timeline_items_contacts` entirely unless you ask for it. Set true when you need to know WHO a note is about.
Clear enough to supply this argument.
start_date
Only notes on or after this bound, inclusive. Accepts a bare calendar date (YYYY-MM-DD) or a full ISO 8601 datetime. A bare date covers that WHOLE day in the account's own timezone; a datetime is used as the exact instant it names. Filters on `event_time` — the note's timeline date, which the user sets freely — NOT on when the note was created.
Clear enough to supply this argument.
end_date
Only notes on or before this bound, inclusive. Accepts a bare calendar date (YYYY-MM-DD) or a full ISO 8601 datetime. A bare date covers that WHOLE day in the account's own timezone; a datetime is used as the exact instant it names. Filters on `event_time` — the note's timeline date, which the user sets freely — NOT on when the note was created.
Describe the returned fields so an agent can interpret the result.
View output schema
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"has_more"
],
"properties": {
"count": {
"type": "number",
"description": "Rows returned on this page"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"note": {
"type": "string",
"description": "The note body"
},
"event_time": {
"type": "string",
"description": "ISO 8601 time the note event occurred"
},
"attachments": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
}
},
"reminder_id": {
"type": "string",
"description": "Linked reminder, when the note has one"
},
"custom_emoji": {
"type": "string"
},
"meeting_type": {
"type": "string",
"description": "Legacy free-text note type label"
},
"meeting_types": {
"type": "object",
"properties": {},
"description": "The joined note type ({ id, name, emoji })",
"additionalProperties": {}
},
"meeting_type_id": {
"type": "string",
"description": "Note type id — matches an entry from dex_list_note_types"
},
"timeline_items_contacts": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
},
"description": "Contacts this note is linked to"
}
},
"additionalProperties": {}
},
"description": "The rows for this page"
},
"has_more": {
"type": "boolean",
"description": "Whether another page is available"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"next_cursor": {
"type": "string",
"description": "Pass as `cursor` to fetch the next page"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_list_notes",
"title": "List Notes",
"description": "List notes on contact timelines with optional filtering by contact and date range.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"limit": {
"type": "number",
"default": 10,
"description": "Results per page (default 10)"
},
"cursor": {
"type": "string",
"description": "Pagination cursor from previous response"
},
"end_date": {
"anyOf": [
{
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
},
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
}
],
"description": "Only notes on or before this bound, inclusive. Accepts a bare calendar date (YYYY-MM-DD) or a full ISO 8601 datetime. A bare date covers that WHOLE day in the account's own timezone; a datetime is used as the exact instant it names. Filters on `event_time` — the note's timeline date, which the user sets freely — NOT on when the note was created."
},
"contact_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Filter by contact ID"
},
"start_date": {
"anyOf": [
{
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
},
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
}
],
"description": "Only notes on or after this bound, inclusive. Accepts a bare calendar date (YYYY-MM-DD) or a full ISO 8601 datetime. A bare date covers that WHOLE day in the account's own timezone; a datetime is used as the exact instant it names. Filters on `event_time` — the note's timeline date, which the user sets freely — NOT on when the note was created."
},
"include_contacts": {
"type": "boolean",
"description": "Include the contacts this note is linked to. Off by default — note rows omit `timeline_items_contacts` entirely unless you ask for it. Set true when you need to know WHO a note is about."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"has_more"
],
"properties": {
"count": {
"type": "number",
"description": "Rows returned on this page"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"note": {
"type": "string",
"description": "The note body"
},
"event_time": {
"type": "string",
"description": "ISO 8601 time the note event occurred"
},
"attachments": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
}
},
"reminder_id": {
"type": "string",
"description": "Linked reminder, when the note has one"
},
"custom_emoji": {
"type": "string"
},
"meeting_type": {
"type": "string",
"description": "Legacy free-text note type label"
},
"meeting_types": {
"type": "object",
"properties": {},
"description": "The joined note type ({ id, name, emoji })",
"additionalProperties": {}
},
"meeting_type_id": {
"type": "string",
"description": "Note type id — matches an entry from dex_list_note_types"
},
"timeline_items_contacts": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
},
"description": "Contacts this note is linked to"
}
},
"additionalProperties": {}
},
"description": "The rows for this page"
},
"has_more": {
"type": "boolean",
"description": "Whether another page is available"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"next_cursor": {
"type": "string",
"description": "Pass as `cursor` to fetch the next page"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description69/100
Get a single note by ID.
Describe the returned information so the agent knows whether it will answer the request.
Inputs75/100
Explain the meaning of this value, not just its name.
note_id
Note ID
Explain the meaning of this value, not just its name.
include_contacts
Include the contacts this note is linked to. Off by default — note rows omit `timeline_items_contacts` entirely unless you ask for it. Set true when you need to know WHO a note is about.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description86/100
Create a new note. Use dex_list_note_types first to discover available note types and pick the most appropriate one. `event_time` is required when `idempotency_key` is supplied; repeat calls with the same key and arguments within the server's 24-hour idempotency window return the original note instead of creating a duplicate.
The description explains the capability, when to use it, and what to expect.
Inputs93/100
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
content
Note content/body
Clear enough to supply this argument.
contact_id
Associate note to a single contact
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
contact_ids
Associate note to multiple contacts at once. Can be combined with contact_id.
Clear enough to supply this argument.
event_time
ISO 8601 datetime for when the note event occurred. Defaults to now if omitted; required with idempotency_key so retries have the same request body.
Clear enough to supply this argument.
note_type_id
Note type ID from dex_list_note_types. Falls back to "Note" type if omitted.
Clear enough to supply this argument.
include_contacts
Include the contacts this note is linked to. Off by default — note rows omit `timeline_items_contacts` entirely unless you ask for it. Set true when you need to know WHO a note is about.
Clear enough to supply this argument.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical note creation.
Describe the returned fields so an agent can interpret the result.
View output schema
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"note": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"note": {
"type": "string",
"description": "The note body"
},
"event_time": {
"type": "string",
"description": "ISO 8601 time the note event occurred"
},
"attachments": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
}
},
"reminder_id": {
"type": "string",
"description": "Linked reminder, when the note has one"
},
"custom_emoji": {
"type": "string"
},
"meeting_type": {
"type": "string",
"description": "Legacy free-text note type label"
},
"meeting_types": {
"type": "object",
"properties": {},
"description": "The joined note type ({ id, name, emoji })",
"additionalProperties": {}
},
"meeting_type_id": {
"type": "string",
"description": "Note type id — matches an entry from dex_list_note_types"
},
"timeline_items_contacts": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
},
"description": "Contacts this note is linked to"
}
},
"description": "The note",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_create_note",
"title": "Create Note",
"description": "Create a new note. Use dex_list_note_types first to discover available note types and pick the most appropriate one. `event_time` is required when `idempotency_key` is supplied; repeat calls with the same key and arguments within the server's 24-hour idempotency window return the original note instead of creating a duplicate.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"content"
],
"properties": {
"content": {
"type": "string",
"description": "Note content/body"
},
"contact_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Associate note to a single contact"
},
"event_time": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "ISO 8601 datetime for when the note event occurred. Defaults to now if omitted; required with idempotency_key so retries have the same request body."
},
"contact_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"description": "Associate note to multiple contacts at once. Can be combined with contact_id."
},
"note_type_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Note type ID from dex_list_note_types. Falls back to \"Note\" type if omitted."
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical note creation."
},
"include_contacts": {
"type": "boolean",
"description": "Include the contacts this note is linked to. Off by default — note rows omit `timeline_items_contacts` entirely unless you ask for it. Set true when you need to know WHO a note is about."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"note": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"note": {
"type": "string",
"description": "The note body"
},
"event_time": {
"type": "string",
"description": "ISO 8601 time the note event occurred"
},
"attachments": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
}
},
"reminder_id": {
"type": "string",
"description": "Linked reminder, when the note has one"
},
"custom_emoji": {
"type": "string"
},
"meeting_type": {
"type": "string",
"description": "Legacy free-text note type label"
},
"meeting_types": {
"type": "object",
"properties": {},
"description": "The joined note type ({ id, name, emoji })",
"additionalProperties": {}
},
"meeting_type_id": {
"type": "string",
"description": "Note type id — matches an entry from dex_list_note_types"
},
"timeline_items_contacts": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
},
"description": "Contacts this note is linked to"
}
},
"description": "The note",
"additionalProperties": {}
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description58/100
Update an existing note.
Explain mutations, irreversible effects, or prerequisites that matter before calling this tool.
Inputs75/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
note_id
Note ID
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
content
New note content
Explain the meaning of this value, not just its name.
event_time
New ISO 8601 datetime for when the event occurred
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
include_contacts
Include the contacts this note is linked to. Off by default — note rows omit `timeline_items_contacts` entirely unless you ask for it. Set true when you need to know WHO a note is about.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_delete_note",
"title": "Delete Note",
"description": "Delete a note. This action is irreversible.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"note_id"
],
"properties": {
"note_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Note ID to delete"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description93/100
Delete a custom field definition. This action is irreversible. Deleting an ID that does not exist — including one you already deleted — returns a not-found error, NOT a success. Do not retry a delete that already succeeded; treat the first success as final.
The description explains the capability, when to use it, and what to expect.
Inputs63/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
custom_field_id
Custom field ID to delete
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_delete_custom_field",
"title": "Delete Custom Field",
"description": "Delete a custom field definition. This action is irreversible. Deleting an ID that does not exist — including one you already deleted — returns a not-found error, NOT a success. Do not retry a delete that already succeeded; treat the first success as final.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"custom_field_id"
],
"properties": {
"custom_field_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Custom field ID to delete"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
Expand internal abbreviations and remove technical prefixes from the display title.
Description78/100
Batch-update custom field values on contacts. Each update has a mode: "replace" (the default) overwrites the stored value; "prepend" / "append" preserve the existing text atomically on the server and add the new text in front of / after it, joined by `separator` (defaults to a newline) — you do NOT need to read the current value first. Array values prepend/append as a de-duplicated list; date values only support replace.
The description explains the capability, when to use it, and what to expect.
Inputs63/100
6 of 8 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
idempotency_key
Stable caller-chosen retry key. Reuse it only for retries of the same logical operation.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
updates › items › contact_id
Not provided
Missing description. Explain what this value means and how to supply it.
updates › items › custom_field_id
Not provided
Missing description. Explain what this value means and how to supply it.
updates › items › text_value
Value for text/autocomplete fields
Clear enough to supply this argument.
updates › items › date_value
Value for date fields (YYYY-MM-DD)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
updates › items › array_value
Value for multi-select fields
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
updates › items › mode
How the value combines with what is stored. "replace" (default) overwrites. "prepend" / "append" keep the existing text and add the new text before / after it atomically — no read needed. Arrays are concatenated and de-duplicated. date_value only supports "replace".
Clear enough to supply this argument.
updates › items › separator
Joiner between the existing and the new text for prepend/append. Defaults to a newline. Ignored for replace and for arrays.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"count"
],
"properties": {
"count": {
"type": "number",
"description": "Number of contact custom-field values written"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_set_custom_field_values",
"title": "Set Custom Field Values",
"description": "Batch-update custom field values on contacts. Each update has a mode: \"replace\" (the default) overwrites the stored value; \"prepend\" / \"append\" preserve the existing text atomically on the server and add the new text in front of / after it, joined by `separator` (defaults to a newline) — you do NOT need to read the current value first. Array values prepend/append as a de-duplicated list; date values only support replace.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"updates"
],
"properties": {
"updates": {
"type": "array",
"items": {
"type": "object",
"required": [
"contact_id",
"custom_field_id"
],
"properties": {
"mode": {
"enum": [
"replace",
"prepend",
"append"
],
"type": "string",
"description": "How the value combines with what is stored. \"replace\" (default) overwrites. \"prepend\" / \"append\" keep the existing text and add the new text before / after it atomically — no read needed. Arrays are concatenated and de-duplicated. date_value only supports \"replace\"."
},
"separator": {
"type": "string",
"maxLength": 20,
"description": "Joiner between the existing and the new text for prepend/append. Defaults to a newline. Ignored for replace and for arrays."
},
"contact_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"date_value": {
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "Value for date fields (YYYY-MM-DD)"
},
"text_value": {
"type": "string",
"description": "Value for text/autocomplete fields"
},
"array_value": {
"type": "array",
"items": {
"type": "string"
},
"description": "Value for multi-select fields"
},
"custom_field_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
}
},
"minItems": 1,
"description": "Array of {contact_id, custom_field_id, text_value or date_value or array_value, mode?, separator?}"
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Stable caller-chosen retry key. Reuse it only for retries of the same logical operation."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"count"
],
"properties": {
"count": {
"type": "number",
"description": "Number of contact custom-field values written"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description93/100
List events across ALL of the user's connected calendars (Google/Microsoft) within a time range. Defaults to the next 7 days when no range is given. An optional free-text `query` searches titles, descriptions, locations, and attendees — combine it with a wide start/end range to find past or future meetings matching a person or company. Events are fetched live from the providers. A failure across all accounts returns an error; warnings indicate incomplete results when only some accounts failed. Each event's `email` field identifies which connected account it belongs to — pass that as `account_email` to get/update/delete it.
The description explains the capability, when to use it, and what to expect.
Inputs94/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
start
Range start, ISO 8601 with offset (e.g. "2026-07-06T00:00:00Z"). Defaults to now.
Clear enough to supply this argument.
end
Range end, ISO 8601 with offset. Defaults to start + 7 days.
Clear enough to supply this argument.
query
Free-text filter matched against event titles, descriptions, locations, and attendees (plain keywords — no search operators).
Clear enough to supply this argument.
limit
Max events PER connected calendar, NOT a total — with two accounts a limit of 3 can return 6. Providers truncate newest-first, so a small limit returns the LATEST events in the range rather than the next ones; narrow `start`/`end` instead of relying on `limit` to mean "the next N". Results are merged in chronological order. (default 50, max 250)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"count"
],
"properties": {
"count": {
"type": "number",
"description": "Number of events returned"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"email": {
"type": "string",
"description": "The connected account this event belongs to — pass as `account_email`"
},
"iCalUID": {
"type": "string"
},
"dateTime": {
"type": "string",
"description": "Start time (ISO 8601)"
},
"isAllDay": {
"type": "boolean"
},
"provider": {
"type": "string",
"description": "GOOGLE or OFFICE365"
},
"providerId": {
"type": "string",
"description": "Provider event id — pass this to get/update/delete"
},
"endDateTime": {
"type": "string",
"description": "End time, for timed (non-all-day) events"
},
"isRecurring": {
"type": "boolean"
},
"participants": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
},
"description": "Attendees, as { email, name }"
},
"providerLink": {
"type": "string",
"description": "Link to the event in the provider UI"
},
"conferenceData": {
"type": "object",
"properties": {},
"description": "Conferencing/join details",
"additionalProperties": {}
},
"subjectOrTitle": {
"type": "string",
"description": "Event title"
},
"summaryOfEvent": {
"type": "string"
},
"descriptionOfEvent": {
"type": "string"
}
},
"additionalProperties": {}
},
"description": "The events in range"
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Present when some connected calendars failed; results are incomplete."
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_list_calendar_events",
"title": "List Calendar Events",
"description": "List events across ALL of the user's connected calendars (Google/Microsoft) within a time range. Defaults to the next 7 days when no range is given. An optional free-text `query` searches titles, descriptions, locations, and attendees — combine it with a wide start/end range to find past or future meetings matching a person or company. Events are fetched live from the providers. A failure across all accounts returns an error; warnings indicate incomplete results when only some accounts failed. Each event's `email` field identifies which connected account it belongs to — pass that as `account_email` to get/update/delete it.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"end": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "Range end, ISO 8601 with offset. Defaults to start + 7 days."
},
"limit": {
"type": "integer",
"maximum": 250,
"minimum": 1,
"description": "Max events PER connected calendar, NOT a total — with two accounts a limit of 3 can return 6. Providers truncate newest-first, so a small limit returns the LATEST events in the range rather than the next ones; narrow `start`/`end` instead of relying on `limit` to mean \"the next N\". Results are merged in chronological order. (default 50, max 250)"
},
"query": {
"type": "string",
"minLength": 1,
"description": "Free-text filter matched against event titles, descriptions, locations, and attendees (plain keywords — no search operators)."
},
"start": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "Range start, ISO 8601 with offset (e.g. \"2026-07-06T00:00:00Z\"). Defaults to now."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"count"
],
"properties": {
"count": {
"type": "number",
"description": "Number of events returned"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"email": {
"type": "string",
"description": "The connected account this event belongs to — pass as `account_email`"
},
"iCalUID": {
"type": "string"
},
"dateTime": {
"type": "string",
"description": "Start time (ISO 8601)"
},
"isAllDay": {
"type": "boolean"
},
"provider": {
"type": "string",
"description": "GOOGLE or OFFICE365"
},
"providerId": {
"type": "string",
"description": "Provider event id — pass this to get/update/delete"
},
"endDateTime": {
"type": "string",
"description": "End time, for timed (non-all-day) events"
},
"isRecurring": {
"type": "boolean"
},
"participants": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
},
"description": "Attendees, as { email, name }"
},
"providerLink": {
"type": "string",
"description": "Link to the event in the provider UI"
},
"conferenceData": {
"type": "object",
"properties": {},
"description": "Conferencing/join details",
"additionalProperties": {}
},
"subjectOrTitle": {
"type": "string",
"description": "Event title"
},
"summaryOfEvent": {
"type": "string"
},
"descriptionOfEvent": {
"type": "string"
}
},
"additionalProperties": {}
},
"description": "The events in range"
},
"warnings": {
"type": "array",
"items": {
"type": "string"
},
"description": "Present when some connected calendars failed; results are incomplete."
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description94/100
Get a single calendar event by its provider event ID, including attendees, conferencing links, and recurrence flags, fetched live from the connected Google/Microsoft account. This tool writes nothing: it opts out of the note-metadata refresh the API performs for other clients. When the same address is connected under both providers, pass account_provider from the event returned by dex_list_calendar_events.
The description explains the capability, when to use it, and what to expect.
Inputs83/100
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
event_id
Provider event ID
Clarify units, where to obtain this value, or conventions not already expressed in the schema.
account_email
Target a specific connected calendar account by its email. Defaults to the primary connected account.
Clear enough to supply this argument.
account_provider
Disambiguates `account_email` when the same address is connected under both providers — take it from the event's `provider` field. Omit otherwise.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description82/100
Create an event on the user's connected calendar (primary account by default). For a timed event set start_datetime + end_datetime; for an all-day event set start_date (end_date optional for multi-day). Attendees are added to the invite; whether they are emailed follows the calendar provider's default behavior.
Describe the returned information so the agent knows whether it will answer the request.
Inputs83/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
summary
Event title
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
description
Event description/notes
Explain the meaning of this value, not just its name.
location
Event location
Explain the meaning of this value, not just its name.
attendees
Attendee email addresses to invite
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Caller-chosen operation key, retained for 24 hours. Reuse only with identical arguments to retry the same operation; use a new key for every new edit or intended new event.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
start_datetime
Start time for a TIMED event, ISO 8601 with offset (e.g. "2026-07-10T14:00:00-07:00"). Requires end_datetime.
Clear enough to supply this argument.
end_datetime
End time for a TIMED event, ISO 8601 with offset.
Clear enough to supply this argument.
timezone
IANA timezone for the event (e.g. "America/New_York").
Explain what happens when this optional value is omitted.
start_date
Start date for an ALL-DAY event (YYYY-MM-DD). Use instead of start_datetime/end_datetime.
Clear enough to supply this argument.
end_date
End date for an ALL-DAY event (YYYY-MM-DD, exclusive). Omit for a single-day event — it defaults to the day after start_date.
Clear enough to supply this argument.
account_email
Target a specific connected calendar account by its email. Defaults to the primary connected account.
Clear enough to supply this argument.
account_provider
Disambiguates `account_email` when the same address is connected under both providers — take it from the event's `provider` field. Omit otherwise.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description86/100
Update an existing calendar event by its provider event ID. Only provided fields change. To reschedule, provide BOTH start_datetime and end_datetime (or start_date for all-day) — new times replace the old ones and can switch an event between timed and all-day. Recurring events: this edits the whole series when given the series ID. Supply idempotency_key from the first attempt to safely retry a timed-out operation; use a fresh key for a new edit.
The description explains the capability, when to use it, and what to expect.
Inputs88/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
event_id
Provider event ID to update
Clear enough to supply this argument.
summary
New event title
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
description
New description/notes
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
location
New location
Explain the meaning of this value, not just its name.
attendees
New attendee list (REPLACES the existing list — include everyone who should stay)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotency_key
Caller-chosen operation key, retained for 24 hours. Reuse only with identical arguments to retry the same operation; use a new key for every new edit or intended new event.
Clear enough to supply this argument.
start_datetime
Start time for a TIMED event, ISO 8601 with offset (e.g. "2026-07-10T14:00:00-07:00"). Requires end_datetime.
Clear enough to supply this argument.
end_datetime
End time for a TIMED event, ISO 8601 with offset.
Clear enough to supply this argument.
timezone
IANA timezone for the event (e.g. "America/New_York").
Clear enough to supply this argument.
start_date
Start date for an ALL-DAY event (YYYY-MM-DD). Use instead of start_datetime/end_datetime.
Explain what happens when this optional value is omitted.
end_date
End date for an ALL-DAY event (YYYY-MM-DD, exclusive). Omit for a single-day event — it defaults to the day after start_date.
Clear enough to supply this argument.
account_email
Target a specific connected calendar account by its email. Defaults to the primary connected account.
Clear enough to supply this argument.
account_provider
Disambiguates `account_email` when the same address is connected under both providers — take it from the event's `provider` field. Omit otherwise.
Describe the returned fields so an agent can interpret the result.
View output schema
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"type": {
"type": "string"
},
"email": {
"type": "string",
"description": "The connected account this event belongs to — pass as `account_email`"
},
"iCalUID": {
"type": "string"
},
"dateTime": {
"type": "string",
"description": "Start time (ISO 8601)"
},
"isAllDay": {
"type": "boolean"
},
"provider": {
"type": "string",
"description": "GOOGLE or OFFICE365"
},
"providerId": {
"type": "string",
"description": "Provider event id — pass this to get/update/delete"
},
"endDateTime": {
"type": "string",
"description": "End time, for timed (non-all-day) events"
},
"isRecurring": {
"type": "boolean"
},
"participants": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
},
"description": "Attendees, as { email, name }"
},
"providerLink": {
"type": "string",
"description": "Link to the event in the provider UI"
},
"conferenceData": {
"type": "object",
"properties": {},
"description": "Conferencing/join details",
"additionalProperties": {}
},
"subjectOrTitle": {
"type": "string",
"description": "Event title"
},
"summaryOfEvent": {
"type": "string"
},
"descriptionOfEvent": {
"type": "string"
}
},
"additionalProperties": {}
}
Original tool metadata
{
"name": "dex_update_calendar_event",
"title": "Update Calendar Event",
"description": "Update an existing calendar event by its provider event ID. Only provided fields change. To reschedule, provide BOTH start_datetime and end_datetime (or start_date for all-day) — new times replace the old ones and can switch an event between timed and all-day. Recurring events: this edits the whole series when given the series ID. Supply idempotency_key from the first attempt to safely retry a timed-out operation; use a fresh key for a new edit.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"event_id"
],
"properties": {
"summary": {
"type": "string",
"minLength": 1,
"description": "New event title"
},
"end_date": {
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "End date for an ALL-DAY event (YYYY-MM-DD, exclusive). Omit for a single-day event — it defaults to the day after start_date."
},
"event_id": {
"type": "string",
"minLength": 1,
"description": "Provider event ID to update"
},
"location": {
"type": "string",
"description": "New location"
},
"timezone": {
"type": "string",
"description": "IANA timezone for the event (e.g. \"America/New_York\")."
},
"attendees": {
"type": "array",
"items": {
"type": "string",
"format": "email"
},
"maxItems": 100,
"description": "New attendee list (REPLACES the existing list — include everyone who should stay)"
},
"start_date": {
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "Start date for an ALL-DAY event (YYYY-MM-DD). Use instead of start_datetime/end_datetime."
},
"description": {
"type": "string",
"description": "New description/notes"
},
"end_datetime": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "End time for a TIMED event, ISO 8601 with offset."
},
"account_email": {
"type": "string",
"description": "Target a specific connected calendar account by its email. Defaults to the primary connected account."
},
"start_datetime": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "Start time for a TIMED event, ISO 8601 with offset (e.g. \"2026-07-10T14:00:00-07:00\"). Requires end_datetime."
},
"idempotency_key": {
"type": "string",
"pattern": "^[\\w:.-]+$",
"maxLength": 200,
"minLength": 1,
"description": "Caller-chosen operation key, retained for 24 hours. Reuse only with identical arguments to retry the same operation; use a new key for every new edit or intended new event."
},
"account_provider": {
"enum": [
"GOOGLE",
"OFFICE365"
],
"type": "string",
"description": "Disambiguates `account_email` when the same address is connected under both providers — take it from the event's `provider` field. Omit otherwise."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"type": {
"type": "string"
},
"email": {
"type": "string",
"description": "The connected account this event belongs to — pass as `account_email`"
},
"iCalUID": {
"type": "string"
},
"dateTime": {
"type": "string",
"description": "Start time (ISO 8601)"
},
"isAllDay": {
"type": "boolean"
},
"provider": {
"type": "string",
"description": "GOOGLE or OFFICE365"
},
"providerId": {
"type": "string",
"description": "Provider event id — pass this to get/update/delete"
},
"endDateTime": {
"type": "string",
"description": "End time, for timed (non-all-day) events"
},
"isRecurring": {
"type": "boolean"
},
"participants": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
},
"description": "Attendees, as { email, name }"
},
"providerLink": {
"type": "string",
"description": "Link to the event in the provider UI"
},
"conferenceData": {
"type": "object",
"properties": {},
"description": "Conferencing/join details",
"additionalProperties": {}
},
"subjectOrTitle": {
"type": "string",
"description": "Event title"
},
"summaryOfEvent": {
"type": "string"
},
"descriptionOfEvent": {
"type": "string"
}
},
"additionalProperties": {}
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description90/100
Delete a calendar event by its provider event ID. Irreversible — and if the user organizes the event, deleting it cancels the meeting for all attendees.
The description explains the capability, when to use it, and what to expect.
Inputs96/100
Input documentation gives an agent enough context to populate the arguments.
event_id
Provider event ID to delete
Clear enough to supply this argument.
account_email
Target a specific connected calendar account by its email. Defaults to the primary connected account.
Clear enough to supply this argument.
account_provider
Disambiguates `account_email` when the same address is connected under both providers — take it from the event's `provider` field. Omit otherwise.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_delete_calendar_event",
"title": "Delete Calendar Event",
"description": "Delete a calendar event by its provider event ID. Irreversible — and if the user organizes the event, deleting it cancels the meeting for all attendees.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"event_id"
],
"properties": {
"event_id": {
"type": "string",
"minLength": 1,
"description": "Provider event ID to delete"
},
"account_email": {
"type": "string",
"description": "Target a specific connected calendar account by its email. Defaults to the primary connected account."
},
"account_provider": {
"enum": [
"GOOGLE",
"OFFICE365"
],
"type": "string",
"description": "Disambiguates `account_email` when the same address is connected under both providers — take it from the event's `provider` field. Omit otherwise."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"deleted"
],
"properties": {
"deleted": {
"type": "string",
"description": "The id that was deleted"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
Search or list emails across ALL of the user's connected mail accounts (Google/Microsoft), fetched live from the providers. Omit `query` to LIST the most recent emails — set folder:'inbox' for received mail only ("my inbox"); the default includes the user's own sent messages. With `query`, matches plain text against subject, body, and participants — pass keywords like a name, company, or email domain; provider search operators (from:, to:) are neutralized. Date ranges: without `after`/`before`, results cover roughly the last 6 months — when listing, prefer following `next_cursor` over widening the range, and pass bounds only when the request names a period. Returns message metadata (sender, participants, subject, snippet, date, provider link) — not full bodies. Only listings paginate: search results are a single relevance-ranked page (`cursor` with `query` is rejected) — narrow a search with dates or better keywords instead.
The description explains the capability, when to use it, and what to expect.
Inputs83/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
query
Free-text search — a name, company, domain, or topic. Omit to list the most recent emails.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
folder
'inbox' = received mail only (use for inbox requests); 'all' (default) spans the whole mailbox including sent mail.
Clear enough to supply this argument.
after
Only messages after this time, ISO 8601 with offset (e.g. "2026-01-01T00:00:00Z").
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
before
Only messages before this time, ISO 8601 with offset.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
cursor
`next_cursor` from the previous page to continue a LISTING. Not valid with `query`.
Clear enough to supply this argument.
limit
Max messages per page (default 25, max 100)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"has_more"
],
"properties": {
"count": {
"type": "number",
"description": "Rows returned on this page"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"from": {
"type": "object",
"properties": {},
"description": "Sender, as { email, name }",
"additionalProperties": {}
},
"type": {
"type": "string"
},
"email": {
"type": "string",
"description": "The connected account this was fetched through"
},
"snippet": {
"type": "string",
"description": "Short preview — never the full body"
},
"dateTime": {
"type": "string",
"description": "Sent/received time (ISO 8601)"
},
"provider": {
"type": "string",
"description": "GOOGLE or OFFICE365"
},
"threadId": {
"type": "string",
"description": "Provider thread id, for grouping a thread"
},
"providerId": {
"type": "string",
"description": "Provider message id"
},
"participants": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
},
"description": "Everyone on the message (to/cc), as { email, name }"
},
"providerLink": {
"type": "string",
"description": "Link to the message in the provider UI"
},
"subjectOrTitle": {
"type": "string",
"description": "Subject line"
}
},
"additionalProperties": {}
},
"description": "The rows for this page"
},
"has_more": {
"type": "boolean",
"description": "Whether another page is available"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"next_cursor": {
"type": "string",
"description": "Pass as `cursor` to fetch the next page"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_search_emails",
"title": "Search Emails",
"description": "Search or list emails across ALL of the user's connected mail accounts (Google/Microsoft), fetched live from the providers. Omit `query` to LIST the most recent emails — set folder:'inbox' for received mail only (\"my inbox\"); the default includes the user's own sent messages. With `query`, matches plain text against subject, body, and participants — pass keywords like a name, company, or email domain; provider search operators (from:, to:) are neutralized. Date ranges: without `after`/`before`, results cover roughly the last 6 months — when listing, prefer following `next_cursor` over widening the range, and pass bounds only when the request names a period. Returns message metadata (sender, participants, subject, snippet, date, provider link) — not full bodies. Only listings paginate: search results are a single relevance-ranked page (`cursor` with `query` is rejected) — narrow a search with dates or better keywords instead.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"after": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "Only messages after this time, ISO 8601 with offset (e.g. \"2026-01-01T00:00:00Z\")."
},
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Max messages per page (default 25, max 100)"
},
"query": {
"type": "string",
"minLength": 1,
"description": "Free-text search — a name, company, domain, or topic. Omit to list the most recent emails."
},
"before": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
"description": "Only messages before this time, ISO 8601 with offset."
},
"cursor": {
"type": "string",
"description": "`next_cursor` from the previous page to continue a LISTING. Not valid with `query`."
},
"folder": {
"enum": [
"inbox",
"all"
],
"type": "string",
"description": "'inbox' = received mail only (use for inbox requests); 'all' (default) spans the whole mailbox including sent mail."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"has_more"
],
"properties": {
"count": {
"type": "number",
"description": "Rows returned on this page"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"from": {
"type": "object",
"properties": {},
"description": "Sender, as { email, name }",
"additionalProperties": {}
},
"type": {
"type": "string"
},
"email": {
"type": "string",
"description": "The connected account this was fetched through"
},
"snippet": {
"type": "string",
"description": "Short preview — never the full body"
},
"dateTime": {
"type": "string",
"description": "Sent/received time (ISO 8601)"
},
"provider": {
"type": "string",
"description": "GOOGLE or OFFICE365"
},
"threadId": {
"type": "string",
"description": "Provider thread id, for grouping a thread"
},
"providerId": {
"type": "string",
"description": "Provider message id"
},
"participants": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"additionalProperties": {}
},
"description": "Everyone on the message (to/cc), as { email, name }"
},
"providerLink": {
"type": "string",
"description": "Link to the message in the provider UI"
},
"subjectOrTitle": {
"type": "string",
"description": "Subject line"
}
},
"additionalProperties": {}
},
"description": "The rows for this page"
},
"has_more": {
"type": "boolean",
"description": "Whether another page is available"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"next_cursor": {
"type": "string",
"description": "Pass as `cursor` to fetch the next page"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description99/100
Run Dex Research on 1–5 contacts: searches the public web, reads the best pages, and stores a cited research note per contact (one-line summary + "Current focus" / "Background" / "Interests" bullets with [[n]] citations into `sources`) plus structured findings (LinkedIn, website, email, phone) with confidence and evidence. This is a PAID action that takes a minute or more per contact (contacts run in parallel, so the call lasts as long as the slowest one) — confirm with the user before researching a large set, and chunk bigger sets into several calls. A contact researched within the last 30 days returns its cached note at no cost unless `force: true`. A run auto-fills a contact's EMPTY linkedin/website fields from high-confidence findings (reported in `applied`) and preserves populated contact fields; a fresh run replaces the previous stored research note, findings, and review statuses. Email/phone findings come back `pending` for the user to confirm before you apply them with dex_update_contact. `outcome: "in_progress"` means another request is already researching that contact: read it back later with dex_get_contact_research rather than running again. Results are large — research a few contacts per call to stay under the response limit. Rows that are not `ok` are listed first so they survive truncation; an `ok` note that was truncated is still stored and readable with dex_get_contact_research.
The description explains the capability, when to use it, and what to expect.
Inputs88/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
contact_ids
Contact IDs to research (max 5 per call; duplicates collapsed)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
force
true re-runs research even when a note under 30 days old is stored (a second paid run)
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"requested",
"ok",
"in_progress",
"failed"
],
"properties": {
"ok": {
"type": "number",
"description": "Contacts whose note is in `items`"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"error": {
"type": "string",
"description": "Present only when `outcome` is `error`"
},
"applied": {
"type": "object",
"properties": {
"website": {
"type": "boolean"
},
"linkedin": {
"type": "boolean"
}
},
"description": "Which empty contact fields this run just filled in",
"additionalProperties": {}
},
"outcome": {
"enum": [
"ok",
"in_progress",
"error"
],
"type": "string",
"description": "`ok`: `research` holds the (fresh or cached) note. `in_progress`: another request is already researching this contact — read it back later with dex_get_contact_research. `error`: see `error`."
},
"research": {
"type": "object",
"properties": {
"fields": {
"type": "array",
"items": {
"type": "object",
"properties": {
"field": {
"enum": [
"linkedin",
"website",
"email",
"phone"
],
"type": "string"
},
"value": {
"type": "string",
"description": "Normalized value"
},
"status": {
"enum": [
"pending",
"auto_applied",
"applied",
"already_present"
],
"type": "string",
"description": "`auto_applied`: the run filled an empty linkedin/website field. `applied`: a person accepted this finding in the Dex UI. `pending`: not on the contact yet — apply with dex_update_contact after confirming with the user. `already_present`: the contact already held this exact value."
},
"display": {
"type": "string",
"description": "The value as it appeared on the source page"
},
"evidence": {
"type": "string",
"description": "One line of why the run believes this"
},
"citations": {
"type": "array",
"items": {
"type": "number"
},
"description": "Keys of `sources` backing this value"
},
"confidence": {
"enum": [
"high",
"medium",
"low"
],
"type": "string"
}
},
"additionalProperties": {}
},
"description": "Structured contact-detail findings discovered on the web"
},
"reason": {
"enum": [
"missing_name",
"fetch_failed",
"insufficient_info",
"wrong_person_suspected"
],
"type": "string",
"description": "Why a `no_data_found` run stopped; absent on success"
},
"run_id": {
"type": "string",
"description": "Identifier of the run that produced this note"
},
"status": {
"enum": [
"success",
"no_data_found"
],
"type": "string",
"description": "Whether the run found usable public information"
},
"sources": {
"type": "object",
"description": "Citation registry keyed by the `n` in `[[n]]` markers",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"title": {
"type": "string"
}
},
"additionalProperties": {}
}
},
"sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"enum": [
"current_focus",
"background",
"interests"
],
"type": "string"
},
"title": {
"type": "string"
},
"bullets": {
"type": "array",
"items": {
"type": "string"
},
"description": "Bullets carrying `[[n]]` citation markers into `sources`"
}
},
"additionalProperties": {}
},
"description": "The research note, in display order; empty sections are omitted"
},
"researched_at": {
"type": "string",
"description": "When the stored note was produced (ISO 8601)"
},
"one_line_summary": {
"type": "string",
"description": "Who they are and why they matter, one sentence, with `[[n]]` citation markers into `sources`"
},
"identity_confidence": {
"enum": [
"high",
"medium",
"low"
],
"type": "string",
"description": "How sure the run is that the sources describe THIS person"
}
},
"description": "A stored research note. Fields whose value is null are omitted, so treat every field as optional.",
"additionalProperties": {}
},
"contact_id": {
"type": "string"
}
},
"additionalProperties": {}
},
"description": "One entry per requested contact; `error` and `in_progress` rows come before `ok` rows"
},
"failed": {
"type": "number",
"description": "Contacts whose run failed"
},
"requested": {
"type": "number",
"description": "Distinct contact IDs requested"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"in_progress": {
"type": "number",
"description": "Contacts another request is still researching"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_research_contacts",
"title": "Research Contacts on the Web",
"description": "Run Dex Research on 1–5 contacts: searches the public web, reads the best pages, and stores a cited research note per contact (one-line summary + \"Current focus\" / \"Background\" / \"Interests\" bullets with [[n]] citations into `sources`) plus structured findings (LinkedIn, website, email, phone) with confidence and evidence. This is a PAID action that takes a minute or more per contact (contacts run in parallel, so the call lasts as long as the slowest one) — confirm with the user before researching a large set, and chunk bigger sets into several calls. A contact researched within the last 30 days returns its cached note at no cost unless `force: true`. A run auto-fills a contact's EMPTY linkedin/website fields from high-confidence findings (reported in `applied`) and preserves populated contact fields; a fresh run replaces the previous stored research note, findings, and review statuses. Email/phone findings come back `pending` for the user to confirm before you apply them with dex_update_contact. `outcome: \"in_progress\"` means another request is already researching that contact: read it back later with dex_get_contact_research rather than running again. Results are large — research a few contacts per call to stay under the response limit. Rows that are not `ok` are listed first so they survive truncation; an `ok` note that was truncated is still stored and readable with dex_get_contact_research.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"contact_ids"
],
"properties": {
"force": {
"type": "boolean",
"default": false,
"description": "true re-runs research even when a note under 30 days old is stored (a second paid run)"
},
"contact_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"maxItems": 5,
"minItems": 1,
"description": "Contact IDs to research (max 5 per call; duplicates collapsed)"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"requested",
"ok",
"in_progress",
"failed"
],
"properties": {
"ok": {
"type": "number",
"description": "Contacts whose note is in `items`"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"error": {
"type": "string",
"description": "Present only when `outcome` is `error`"
},
"applied": {
"type": "object",
"properties": {
"website": {
"type": "boolean"
},
"linkedin": {
"type": "boolean"
}
},
"description": "Which empty contact fields this run just filled in",
"additionalProperties": {}
},
"outcome": {
"enum": [
"ok",
"in_progress",
"error"
],
"type": "string",
"description": "`ok`: `research` holds the (fresh or cached) note. `in_progress`: another request is already researching this contact — read it back later with dex_get_contact_research. `error`: see `error`."
},
"research": {
"type": "object",
"properties": {
"fields": {
"type": "array",
"items": {
"type": "object",
"properties": {
"field": {
"enum": [
"linkedin",
"website",
"email",
"phone"
],
"type": "string"
},
"value": {
"type": "string",
"description": "Normalized value"
},
"status": {
"enum": [
"pending",
"auto_applied",
"applied",
"already_present"
],
"type": "string",
"description": "`auto_applied`: the run filled an empty linkedin/website field. `applied`: a person accepted this finding in the Dex UI. `pending`: not on the contact yet — apply with dex_update_contact after confirming with the user. `already_present`: the contact already held this exact value."
},
"display": {
"type": "string",
"description": "The value as it appeared on the source page"
},
"evidence": {
"type": "string",
"description": "One line of why the run believes this"
},
"citations": {
"type": "array",
"items": {
"type": "number"
},
"description": "Keys of `sources` backing this value"
},
"confidence": {
"enum": [
"high",
"medium",
"low"
],
"type": "string"
}
},
"additionalProperties": {}
},
"description": "Structured contact-detail findings discovered on the web"
},
"reason": {
"enum": [
"missing_name",
"fetch_failed",
"insufficient_info",
"wrong_person_suspected"
],
"type": "string",
"description": "Why a `no_data_found` run stopped; absent on success"
},
"run_id": {
"type": "string",
"description": "Identifier of the run that produced this note"
},
"status": {
"enum": [
"success",
"no_data_found"
],
"type": "string",
"description": "Whether the run found usable public information"
},
"sources": {
"type": "object",
"description": "Citation registry keyed by the `n` in `[[n]]` markers",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"title": {
"type": "string"
}
},
"additionalProperties": {}
}
},
"sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"enum": [
"current_focus",
"background",
"interests"
],
"type": "string"
},
"title": {
"type": "string"
},
"bullets": {
"type": "array",
"items": {
"type": "string"
},
"description": "Bullets carrying `[[n]]` citation markers into `sources`"
}
},
"additionalProperties": {}
},
"description": "The research note, in display order; empty sections are omitted"
},
"researched_at": {
"type": "string",
"description": "When the stored note was produced (ISO 8601)"
},
"one_line_summary": {
"type": "string",
"description": "Who they are and why they matter, one sentence, with `[[n]]` citation markers into `sources`"
},
"identity_confidence": {
"enum": [
"high",
"medium",
"low"
],
"type": "string",
"description": "How sure the run is that the sources describe THIS person"
}
},
"description": "A stored research note. Fields whose value is null are omitted, so treat every field as optional.",
"additionalProperties": {}
},
"contact_id": {
"type": "string"
}
},
"additionalProperties": {}
},
"description": "One entry per requested contact; `error` and `in_progress` rows come before `ok` rows"
},
"failed": {
"type": "number",
"description": "Contacts whose run failed"
},
"requested": {
"type": "number",
"description": "Distinct contact IDs requested"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"in_progress": {
"type": "number",
"description": "Contacts another request is still researching"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description100/100
Read the stored Dex Research note for 1–10 contacts without running anything (free and fast). Returns each contact's cited note and structured findings from its last run, or no `research` key when the contact has never been researched. Use this to check for existing research before paying for dex_research_contacts, to pick up a run that came back `in_progress`, or to re-read results a large run response truncated.
The description explains the capability, when to use it, and what to expect.
Inputs75/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
contact_ids
Contact IDs to read research for (max 10 per call; duplicates collapsed)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"requested",
"found"
],
"properties": {
"found": {
"type": "number",
"description": "Contacts that have a stored note"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"error": {
"type": "string",
"description": "Present when the read failed, e.g. the contact does not exist"
},
"research": {
"type": "object",
"properties": {
"fields": {
"type": "array",
"items": {
"type": "object",
"properties": {
"field": {
"enum": [
"linkedin",
"website",
"email",
"phone"
],
"type": "string"
},
"value": {
"type": "string",
"description": "Normalized value"
},
"status": {
"enum": [
"pending",
"auto_applied",
"applied",
"already_present"
],
"type": "string",
"description": "`auto_applied`: the run filled an empty linkedin/website field. `applied`: a person accepted this finding in the Dex UI. `pending`: not on the contact yet — apply with dex_update_contact after confirming with the user. `already_present`: the contact already held this exact value."
},
"display": {
"type": "string",
"description": "The value as it appeared on the source page"
},
"evidence": {
"type": "string",
"description": "One line of why the run believes this"
},
"citations": {
"type": "array",
"items": {
"type": "number"
},
"description": "Keys of `sources` backing this value"
},
"confidence": {
"enum": [
"high",
"medium",
"low"
],
"type": "string"
}
},
"additionalProperties": {}
},
"description": "Structured contact-detail findings discovered on the web"
},
"reason": {
"enum": [
"missing_name",
"fetch_failed",
"insufficient_info",
"wrong_person_suspected"
],
"type": "string",
"description": "Why a `no_data_found` run stopped; absent on success"
},
"run_id": {
"type": "string",
"description": "Identifier of the run that produced this note"
},
"status": {
"enum": [
"success",
"no_data_found"
],
"type": "string",
"description": "Whether the run found usable public information"
},
"sources": {
"type": "object",
"description": "Citation registry keyed by the `n` in `[[n]]` markers",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"title": {
"type": "string"
}
},
"additionalProperties": {}
}
},
"sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"enum": [
"current_focus",
"background",
"interests"
],
"type": "string"
},
"title": {
"type": "string"
},
"bullets": {
"type": "array",
"items": {
"type": "string"
},
"description": "Bullets carrying `[[n]]` citation markers into `sources`"
}
},
"additionalProperties": {}
},
"description": "The research note, in display order; empty sections are omitted"
},
"researched_at": {
"type": "string",
"description": "When the stored note was produced (ISO 8601)"
},
"one_line_summary": {
"type": "string",
"description": "Who they are and why they matter, one sentence, with `[[n]]` citation markers into `sources`"
},
"identity_confidence": {
"enum": [
"high",
"medium",
"low"
],
"type": "string",
"description": "How sure the run is that the sources describe THIS person"
}
},
"description": "The stored note. Absent when the contact has never been researched.",
"additionalProperties": {}
},
"contact_id": {
"type": "string"
}
},
"additionalProperties": {}
},
"description": "One entry per requested contact"
},
"requested": {
"type": "number",
"description": "Distinct contact IDs requested"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_get_contact_research",
"title": "Get Stored Contact Research",
"description": "Read the stored Dex Research note for 1–10 contacts without running anything (free and fast). Returns each contact's cited note and structured findings from its last run, or no `research` key when the contact has never been researched. Use this to check for existing research before paying for dex_research_contacts, to pick up a run that came back `in_progress`, or to re-read results a large run response truncated.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"contact_ids"
],
"properties": {
"contact_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"maxItems": 10,
"minItems": 1,
"description": "Contact IDs to read research for (max 10 per call; duplicates collapsed)"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"requested",
"found"
],
"properties": {
"found": {
"type": "number",
"description": "Contacts that have a stored note"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"error": {
"type": "string",
"description": "Present when the read failed, e.g. the contact does not exist"
},
"research": {
"type": "object",
"properties": {
"fields": {
"type": "array",
"items": {
"type": "object",
"properties": {
"field": {
"enum": [
"linkedin",
"website",
"email",
"phone"
],
"type": "string"
},
"value": {
"type": "string",
"description": "Normalized value"
},
"status": {
"enum": [
"pending",
"auto_applied",
"applied",
"already_present"
],
"type": "string",
"description": "`auto_applied`: the run filled an empty linkedin/website field. `applied`: a person accepted this finding in the Dex UI. `pending`: not on the contact yet — apply with dex_update_contact after confirming with the user. `already_present`: the contact already held this exact value."
},
"display": {
"type": "string",
"description": "The value as it appeared on the source page"
},
"evidence": {
"type": "string",
"description": "One line of why the run believes this"
},
"citations": {
"type": "array",
"items": {
"type": "number"
},
"description": "Keys of `sources` backing this value"
},
"confidence": {
"enum": [
"high",
"medium",
"low"
],
"type": "string"
}
},
"additionalProperties": {}
},
"description": "Structured contact-detail findings discovered on the web"
},
"reason": {
"enum": [
"missing_name",
"fetch_failed",
"insufficient_info",
"wrong_person_suspected"
],
"type": "string",
"description": "Why a `no_data_found` run stopped; absent on success"
},
"run_id": {
"type": "string",
"description": "Identifier of the run that produced this note"
},
"status": {
"enum": [
"success",
"no_data_found"
],
"type": "string",
"description": "Whether the run found usable public information"
},
"sources": {
"type": "object",
"description": "Citation registry keyed by the `n` in `[[n]]` markers",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"title": {
"type": "string"
}
},
"additionalProperties": {}
}
},
"sections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"enum": [
"current_focus",
"background",
"interests"
],
"type": "string"
},
"title": {
"type": "string"
},
"bullets": {
"type": "array",
"items": {
"type": "string"
},
"description": "Bullets carrying `[[n]]` citation markers into `sources`"
}
},
"additionalProperties": {}
},
"description": "The research note, in display order; empty sections are omitted"
},
"researched_at": {
"type": "string",
"description": "When the stored note was produced (ISO 8601)"
},
"one_line_summary": {
"type": "string",
"description": "Who they are and why they matter, one sentence, with `[[n]]` citation markers into `sources`"
},
"identity_confidence": {
"enum": [
"high",
"medium",
"low"
],
"type": "string",
"description": "How sure the run is that the sources describe THIS person"
}
},
"description": "The stored note. Absent when the contact has never been researched.",
"additionalProperties": {}
},
"contact_id": {
"type": "string"
}
},
"additionalProperties": {}
},
"description": "One entry per requested contact"
},
"requested": {
"type": "number",
"description": "Distinct contact IDs requested"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
List Upcoming Reminders, Birthdays and Keep in Touch
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
What's coming up for the user inside a date window, like the Dex homepage: the contacts whose birthday falls in the window, the open reminders due in it, and the keep-in-touch contacts due for a touch by the end of it. Defaults to the next 7 days starting today — use `days: 30` for the next month, or set `from` to look at a specific week. Use this for questions like "whose birthday is next week?", "who should I reach out to this week?" or "what do I have coming up?". Recurring reminders are listed on their next occurrence. Birthdays the user already marked done this year on the homepage are omitted. `keep_in_touch` DOES include contacts already overdue for a touch (flagged `is_overdue`) — the homepage keeps showing them as due until the touch is logged. Overdue reminders are NOT included — call dex_list_reminders with is_overdue=true for those.
The description explains the capability, when to use it, and what to expect.
Inputs75/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
from
First day of the window, inclusive (YYYY-MM-DD). Defaults to today in UTC — pass the user's local date when it may differ.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
days
Length of the window in days, counting `from` as day one (default 7, max 92). 7 = the coming week, 30 = the coming month.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"window",
"birthdays",
"reminders",
"keep_in_touch"
],
"properties": {
"window": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string"
},
"from": {
"type": "string"
}
},
"description": "The inclusive date window that was searched (YYYY-MM-DD)",
"additionalProperties": false
},
"birthdays": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date": {
"type": "string",
"description": "The day the birthday falls on inside the window (YYYY-MM-DD)"
},
"email": {
"type": "string",
"description": "Primary email, when one is stored"
},
"company": {
"type": "string"
},
"full_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"contact_id": {
"type": "string"
},
"first_name": {
"type": "string"
},
"turning_age": {
"type": "number",
"description": "The age the contact turns on `date`, when the birth year is known"
},
"phone_number": {
"type": "string",
"description": "Primary phone number, when one is stored"
},
"birthday_year": {
"type": "number",
"description": "Birth year, when the contact has one stored"
}
},
"description": "A contact with a birthday inside the window. Fields whose value is null are omitted; use dex_get_contact for the full record.",
"additionalProperties": {}
},
"description": "Contacts with a birthday inside the window, soonest first"
},
"reminders": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string",
"description": "The reminder text — doubles as its title. Clipped to 1000 characters; dex_get_reminder has the full text."
},
"due_on": {
"type": "string",
"description": "The day the reminder comes up inside the window (YYYY-MM-DD): the next occurrence for a recurring reminder, the due date otherwise. Rows are sorted by this."
},
"contacts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"full_name": {
"type": "string"
}
},
"additionalProperties": {}
},
"description": "Contacts this reminder is linked to"
},
"is_overdue": {
"type": "boolean",
"description": "Whether `due_on` is before today (UTC)"
},
"recurrence": {
"type": "string",
"description": "Postgres interval string, e.g. \"7 days\" or \"1 mon\""
},
"due_at_date": {
"type": "string",
"description": "Due date (YYYY-MM-DD)"
},
"due_at_time": {
"type": "string",
"description": "Due time of day, when one is set"
},
"is_complete": {
"type": "boolean"
},
"last_completed_at": {
"type": "string"
},
"next_occurrence_at": {
"type": "string",
"description": "Next occurrence of a recurring reminder (YYYY-MM-DD)"
}
},
"description": "An open reminder due inside the window. Fields whose value is null are omitted.",
"additionalProperties": {}
},
"description": "Open reminders due inside the window, soonest first — recurring ones on their next occurrence"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"keep_in_touch": {
"type": "array",
"items": {
"type": "object",
"properties": {
"due_on": {
"type": "string",
"description": "The day the next touch is due (YYYY-MM-DD). Rows are sorted by this."
},
"cadence": {
"type": "string",
"description": "Keep-in-touch cadence, e.g. \"7 days\" or \"1 mon\""
},
"company": {
"type": "string"
},
"full_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"contact_id": {
"type": "string"
},
"first_name": {
"type": "string"
},
"is_overdue": {
"type": "boolean",
"description": "Whether `due_on` is before today (UTC)"
},
"last_seen_at": {
"type": "string",
"description": "Last interaction with the contact (YYYY-MM-DD), when known"
}
},
"description": "A contact whose keep-in-touch cadence is due by the end of the window. Fields whose value is null are omitted; use dex_get_contact for the full record and dex_complete_keep_in_touch to log the touch.",
"additionalProperties": {}
},
"description": "Contacts with an active keep-in-touch cadence whose next touch is due by the end of the window, soonest first — including ones already overdue (`is_overdue`)"
},
"reminders_truncated": {
"type": "boolean",
"description": "Present and true only when more than 200 one-off reminders fell inside the window and the list was cut. Narrow `days`, or page with dex_list_reminders (due_after / due_before)."
},
"keep_in_touch_truncated": {
"type": "boolean",
"description": "Present and true only when more than 200 keep-in-touch contacts were due by the end of the window and the list was cut. Narrow `days`, or log touches with dex_complete_keep_in_touch and call again."
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "dex_list_upcoming_reminders_and_birthdays",
"title": "List Upcoming Reminders, Birthdays and Keep in Touch",
"description": "What's coming up for the user inside a date window, like the Dex homepage: the contacts whose birthday falls in the window, the open reminders due in it, and the keep-in-touch contacts due for a touch by the end of it. Defaults to the next 7 days starting today — use `days: 30` for the next month, or set `from` to look at a specific week. Use this for questions like \"whose birthday is next week?\", \"who should I reach out to this week?\" or \"what do I have coming up?\". Recurring reminders are listed on their next occurrence. Birthdays the user already marked done this year on the homepage are omitted. `keep_in_touch` DOES include contacts already overdue for a touch (flagged `is_overdue`) — the homepage keeps showing them as due until the touch is logged. Overdue reminders are NOT included — call dex_list_reminders with is_overdue=true for those.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"days": {
"type": "integer",
"default": 7,
"maximum": 92,
"minimum": 1,
"description": "Length of the window in days, counting `from` as day one (default 7, max 92). 7 = the coming week, 30 = the coming month."
},
"from": {
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "First day of the window, inclusive (YYYY-MM-DD). Defaults to today in UTC — pass the user's local date when it may differ."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"window",
"birthdays",
"reminders",
"keep_in_touch"
],
"properties": {
"window": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "string"
},
"from": {
"type": "string"
}
},
"description": "The inclusive date window that was searched (YYYY-MM-DD)",
"additionalProperties": false
},
"birthdays": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date": {
"type": "string",
"description": "The day the birthday falls on inside the window (YYYY-MM-DD)"
},
"email": {
"type": "string",
"description": "Primary email, when one is stored"
},
"company": {
"type": "string"
},
"full_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"contact_id": {
"type": "string"
},
"first_name": {
"type": "string"
},
"turning_age": {
"type": "number",
"description": "The age the contact turns on `date`, when the birth year is known"
},
"phone_number": {
"type": "string",
"description": "Primary phone number, when one is stored"
},
"birthday_year": {
"type": "number",
"description": "Birth year, when the contact has one stored"
}
},
"description": "A contact with a birthday inside the window. Fields whose value is null are omitted; use dex_get_contact for the full record.",
"additionalProperties": {}
},
"description": "Contacts with a birthday inside the window, soonest first"
},
"reminders": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string",
"description": "The reminder text — doubles as its title. Clipped to 1000 characters; dex_get_reminder has the full text."
},
"due_on": {
"type": "string",
"description": "The day the reminder comes up inside the window (YYYY-MM-DD): the next occurrence for a recurring reminder, the due date otherwise. Rows are sorted by this."
},
"contacts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"full_name": {
"type": "string"
}
},
"additionalProperties": {}
},
"description": "Contacts this reminder is linked to"
},
"is_overdue": {
"type": "boolean",
"description": "Whether `due_on` is before today (UTC)"
},
"recurrence": {
"type": "string",
"description": "Postgres interval string, e.g. \"7 days\" or \"1 mon\""
},
"due_at_date": {
"type": "string",
"description": "Due date (YYYY-MM-DD)"
},
"due_at_time": {
"type": "string",
"description": "Due time of day, when one is set"
},
"is_complete": {
"type": "boolean"
},
"last_completed_at": {
"type": "string"
},
"next_occurrence_at": {
"type": "string",
"description": "Next occurrence of a recurring reminder (YYYY-MM-DD)"
}
},
"description": "An open reminder due inside the window. Fields whose value is null are omitted.",
"additionalProperties": {}
},
"description": "Open reminders due inside the window, soonest first — recurring ones on their next occurrence"
},
"_truncated": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"skipped_count": {
"type": "number",
"description": "Rows dropped that `next_cursor` will NOT return; re-request with a smaller limit"
},
"original_count": {
"type": "number"
},
"returned_count": {
"type": "number"
}
},
"description": "Present only when the page was trimmed to fit the response size limit. Paginate with `next_cursor` (or ask for a smaller `limit`) to see the rest.",
"additionalProperties": {}
},
"keep_in_touch": {
"type": "array",
"items": {
"type": "object",
"properties": {
"due_on": {
"type": "string",
"description": "The day the next touch is due (YYYY-MM-DD). Rows are sorted by this."
},
"cadence": {
"type": "string",
"description": "Keep-in-touch cadence, e.g. \"7 days\" or \"1 mon\""
},
"company": {
"type": "string"
},
"full_name": {
"type": "string"
},
"job_title": {
"type": "string"
},
"contact_id": {
"type": "string"
},
"first_name": {
"type": "string"
},
"is_overdue": {
"type": "boolean",
"description": "Whether `due_on` is before today (UTC)"
},
"last_seen_at": {
"type": "string",
"description": "Last interaction with the contact (YYYY-MM-DD), when known"
}
},
"description": "A contact whose keep-in-touch cadence is due by the end of the window. Fields whose value is null are omitted; use dex_get_contact for the full record and dex_complete_keep_in_touch to log the touch.",
"additionalProperties": {}
},
"description": "Contacts with an active keep-in-touch cadence whose next touch is due by the end of the window, soonest first — including ones already overdue (`is_overdue`)"
},
"reminders_truncated": {
"type": "boolean",
"description": "Present and true only when more than 200 one-off reminders fell inside the window and the list was cut. Narrow `days`, or page with dex_list_reminders (due_after / due_before)."
},
"keep_in_touch_truncated": {
"type": "boolean",
"description": "Present and true only when more than 200 keep-in-touch contacts were due by the end of the window and the list was cut. Narrow `days`, or log touches with dex_complete_keep_in_touch and call again."
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
Loading score analytics…
Put your MCP to the test
A few tasks worth trying with this server, chosen from its reviewed tools. These have not been run.
01Search ContactsSearch contacts by name, email, phone number, or any keyword.
02List Upcoming Reminders, Birthdays and Keep in TouchWhat's coming up for the user inside a date window, like the Dex homepage: the contacts whose birthday falls in the window, the open reminders due in it, and…
03List ContactsList all contacts with cursor-based pagination.
04List Calendar EventsList events across ALL of the user's connected calendars (Google/Microsoft) within a time range.
05Search EmailsSearch or list emails across ALL of the user's connected mail accounts (Google/Microsoft), fetched live from the providers.
Want to run these tasks against your MCP server with us?
dex_search_contactsdex_list_contactsdex_get_contactdex_create_contactdex_update_contactdex_delete_contactsdex_merge_contactsdex_list_groupsdex_get_groupdex_create_groupdex_update_groupdex_delete_group+26 more
dex setup, tools, quality score, publishing readiness, and alternatives.
How do I add dex to Claude, ChatGPT, or Cursor?
Add dex to Claude, ChatGPT, or Cursor by connecting its remote MCP URL in each client's MCP settings. In Claude, add a custom connector. In ChatGPT, add an MCP app. In Cursor, add an MCP server under Settings. The MCP URL is https://mcp.getdex.com/mcp. This server requires OAuth, so complete the login prompt after you connect.
Is dex MCP ready for ChatGPT and Claude?
As of October 6, 2026, dex scores 90/100 on Manufact's MCP publishing checklist. The scan does not yet meet ChatGPT publishing checks and meets Claude publishing checks. Passing these checks does not guarantee marketplace approval.
How clear is dex's MCP tool documentation?
As of October 6, 2026, dex scores 85/100 in Manufact's review of tool titles, descriptions, input and output schemas, and behavior annotations. The review covers 53 of 53 discovered tools. The quality score measures documentation clarity; runtime behavior and marketplace readiness require separate checks.
What tools does the dex MCP server provide?
dex lists 38 MCP tools, including dex_search_contacts, dex_list_contacts, dex_get_contact, dex_create_contact, dex_update_contact, dex_delete_contacts, dex_merge_contacts, dex_list_groups, dex_get_group, dex_create_group, dex_update_group, and dex_delete_group.
What are the best alternatives to dex?
As of October 20, 2026, alternatives to dex in sales-and-marketing on the Manufact MCP leaderboard include Griddo AX Docs, Rocket Sloth, Affitor, Affonso.io, and Tolt.
Does dex MCP require authentication?
dex requires authentication. Connect it in Claude, ChatGPT, or Cursor and complete the OAuth login prompt.
How can dex improve its MCP documentation?
dex_update_reminder is the lowest-scoring reviewed tool at 70/100. Explain mutations, irreversible effects, or prerequisites that matter before calling this tool. Open its review for feedback and a copyable fix prompt, then rerun the documentation review after updating your tool metadata.
Who develops dex?
dex is developed by Dex. It is listed as community on the Claude connector directory.
Not your MCP?
Review any public or OAuth MCP server for protocol compatibility, tool documentation, and ChatGPT and Claude publishing readiness. No account required.