Coevera MCP Server v2.0.9

Model Context Protocol server exposing Coevera CRM data and operations as AI-ready tools.

What is MCP?

Model Context Protocol (MCP) is an open standard that lets AI assistants (such as Claude, ChatGPT, or any MCP-compatible agent) call structured tools hosted on a remote server over HTTP. Instead of copy-pasting data into a chat window, the AI connects to this server and reads, searches, or updates CRM records directly.

This server implements the Streamable HTTP MCP transport. Requests are POSTed to the /mcp endpoint with a Bearer token. The AI model discovers what tools are available, then calls them with structured JSON arguments.

Long-running tool calls stream incremental progress notifications back to the client via Server-Sent Events (SSE). The client receives status messages such as "Fetching records…" and "Normalizing records…" before the final result, so the user sees live feedback instead of a blank wait. SSE is activated automatically when the client sends Accept: text/event-stream in the request (all MCP-compliant clients do this); clients that omit the header receive a plain JSON response.

Each MCP session is stateless — authentication is re-validated on every request via OAuth token introspection.

Intended Purpose

This server bridges AI assistants (such as Claude, ChatGPT, or any MCP-compatible agent) with the Coevera CRM. Instead of manually exporting data and pasting it into a chat window, the AI connects here and reads, searches, or updates CRM records directly using structured tool calls.

Typical workflows

  1. Ask the AI to find all Accounts in Germany → calls search_records
  2. Ask the AI to summarize open Opportunities → calls search_records + get_records
  3. Ask the AI to create a follow-up Task for a Contact → calls create_records
  4. Ask the AI to attach this PDF to the Contracts folder → calls upload_cloud_object_document, then POSTs the file to the returned upload_url
  5. Ask the AI what fields does Lead have? → calls meta_describe_entity
Recommended discovery order: meta_list_entity_types → meta_describe_entity → search_records / get_records

How It Works

Every MCP tool call follows this path from the AI assistant to the CRM and back:

#StepComponent
1 The AI assistant posts a tool-call JSON to POST /mcp with an Authorization: Bearer <token> header HTTP / Streamable MCP transport
2 Token is introspected against the Coevera OAuth server; required scopes are verified and the tenant space_id is extracted BearerAuthMiddleware
3 Request is checked against burst and sustained rate limits (when enabled) RateLimitMiddleware
4 FastMCP routes the call to the named tool handler core/tools/simple.py
5 Tool handler validates inputs and sends an initial notifications/progress SSE event to the client (e.g. "Fetching records…"), then delegates to the business logic layer core/tools/simple.py
6 PipelinerService queries the Coevera CRM REST / GraphQL API (with automatic retry on transient failures) core/pipeliner_client.py
7 Response is normalized to an LLM-friendly JSON structure; a final progress event is sent, then the result is returned to the AI assistant core/llm_view.py

Each request is fully stateless — no session is maintained between calls. The space_id tenant identifier is re-extracted from the token on every request.

External Connections & Ownership

Service Domain / URL Purpose Required Ownership
Coevera CRM — REST APIcrm.coevera.comEntity CRUD operations (read, create, update, delete records).requiredOwned — Pipeliner Sales Inc. operates crm.coevera.com.
Coevera CRM — GraphQL APIcrm.coevera.comSpace and schema discovery queries.requiredOwned — same domain as above.
Coevera OAuth 2.0 serverhttps://crm.coevera.com/oauth2/app/authorizeAuthorization, token issuance, and bearer-token introspection.requiredOwned — OAuth server is part of the Coevera CRM platform.

Authentication & OAuth

All /mcp requests must carry an Authorization: Bearer <token> header. Tokens are obtained from the Coevera OAuth2 authorization server using the Authorization Code flow.

OAuth provider (CRM)
https://crm.coevera.com
Issuer (RFC 8414)
https://mcp-server.coevera.com  (this server — it serves the discovery documents, so the issuer must match its origin)
Authorization endpoint
https://crm.coevera.com/oauth2/app/authorize
Token endpoint
https://crm.coevera.com/oauth2/app/token
Introspection endpoint
https://crm.coevera.com/oauth2/app/introspection
Grant type
authorization_code
Auth methods
client_secret_basic, client_secret_post

Required scopes

contacts.fullaccounts.fullleads.fulltasks.fullemails.fullappointments.fullmessages.fullopportunities.fullprojects.readcurrencies.readproducts_and_prices.fullpipelines.readfields_and_forms.readdocuments.fullunits_and_roles.read

OAuth discovery documents are served at /.well-known/oauth-authorization-server and /.well-known/openid-configuration.

Server Endpoints

CRM base URL
https://crm.coevera.com/
MCP endpoint
/mcp  (POST — requires Bearer token; returns SSE stream when client sends Accept: text/event-stream, plain JSON otherwise)
File upload
/upload/{token}  (POST multipart/form-data, field file — no Bearer header; the single-use token issued by upload_cloud_object_document is the credential. 410 if expired/used, 503 if upload store not configured)
Health check
/lbready  (GET — public)
This page
/doc  (GET — public)
OAuth discovery
/.well-known/oauth-authorization-server
OpenID config
/.well-known/openid-configuration
Protected resource
/.well-known/oauth-protected-resource

MCP Tools

The server exposes 16 tools. Read-only tools are safe to call at any time. Write tools (write) modify CRM data.

me — Current User Identity read-only

Returns identity information for the authenticated user: connected Pipeliner space (space_id), user ID, OAuth client ID, granted scopes, token expiry (ISO 8601 UTC), and resource audience. No CRM API call is made — all data is derived from the validated Bearer token. Invoke when user identity, connected space, or OAuth permissions need to be determined.

Parameters

NameTypeRequiredDescription
No parameters
Returns:JSON: {space_id, user_id, client_id, scopes[], expires_at, resource}
meta_list_entity_types — List Entity Types read-only

Returns all entity types available in the CRM for the authenticated user. Each entry includes the entity name, display label, kind (primary or relation), api_name, custom_name, description, and the user's CRUD permissions.

Parameters

NameTypeRequiredDescription
No parameters
Returns:JSON array of entity descriptors with permissions.
meta_describe_entity — Describe Entity read-only

Returns the field schema for a named CRM entity. Each field entry contains: api_name, label, data type, required, read_only, filterable, default_value, and — for dropdown fields — an options list. When the entity has expandable relation fields, the response also includes an expandable_fields array. Each entry has api_name, entity_name (the related entity), and type (object for single-record relations, list for multi-record relations). This includes both built-in relations and custom lookup fields created in the CRM UI.

Parameters

NameTypeRequiredDescription
entity_name*stringRequiredThe entity name (e.g. Account).
Returns:JSON object with entity metadata, a fields array, an optional expandable_fields array listing relation fields that can be passed to the expand parameter of search_records or get_records, and an optional ownership object describing how ownership can be transferred for this entity: supported (bool), requires_unit (bool), options (list of valid keys for the change_ownership tool's options parameter), and an optional warning string for entities where the service account loses access after transfer. The ownership key is omitted entirely when ownership transfer is not supported.
search_records — Search Records read-only

Searches a CRM entity and returns matching records for the current page. Supports boolean filter trees (AND/OR/NOT), typed field comparators, sorting, and cursor-based pagination.

Parameters

NameTypeRequiredDescription
entity_name*stringRequiredTarget entity.
fields*string[]RequiredField api_names to include in each record.
filterobject|nullOptionalBoolean filter tree. Logical nodes: AND / OR (array of child nodes), NOT (single child node). Leaf node: {"<field>": {"<op>": value}}. Omit to match all records. See Filter DSL reference below.
order_byarray|nullOptionalList of sort objects, each mapping one field api_name to "Asc" or "Desc". Example: [{"modified": "Desc"}, {"name": "Asc"}].
limitintOptionalPage size (default 100, max 100).
afterstring|nullOptionalPagination cursor from a previous response's next_cursor.
expandobject|nullOptionalLoad related entities inline. Values must be non-empty sub-field lists. Flat key: {"product_relations": ["id", "product_id"]}. Dotted key: {"product_relations.product": ["name", "id"]} — expands a nested relation inside the parent with explicit sub-fields. Both forms can be combined. See Expand Fields reference.
Returns:JSON: {entity, total, count, records[], pagination: {has_next_page, next_cursor}}
get_records — Get Records by ID read-only

Fetches specific records by ID from a CRM entity. Returns all matching records as a plain list — no sample cap, no schema inference.

Parameters

NameTypeRequiredDescription
entity_name*stringRequiredTarget entity.
record_ids*string[]RequiredList of record UUIDs to fetch.
fields*string[]RequiredField api_names to include.
expandobject|nullOptionalLoad related entities inline. Same format as search_records. See Expand Fields reference.
Returns:JSON: {entity, count, records[]}
create_records — Create Records write

Creates one or more records in a CRM entity. Each record is a dict of field api_name → value pairs.

Parameters

NameTypeRequiredDescription
entity_name*stringRequiredTarget entity.
records*object[]RequiredList of record dicts (field api_name → value).
Returns:{"results": [...]} — one entry per input record, in the same order.
OutcomeShape
Success{"id": "...", "url": "..."}
Read-only field violation{"error": "read_only_field_violation", "message": "...", "violations": ["field", ...], "id": "..."}
CRM API error{"code": "...", ...}
Tool error{"error": "invalid_input" | "permission_denied" | "api_error" | "server_error", "message": "..."}
update_records — Update Records write

Partially updates existing records in a CRM entity. Only the fields provided in each record dict are changed. Each record dict must include the record's id field.

Parameters

NameTypeRequiredDescription
entity_name*stringRequiredTarget entity.
records*object[]RequiredList of record dicts, each must contain "id".
Returns:{"results": [...]} — one entry per input record, in the same order. Records with errors are skipped; valid records are still submitted.
OutcomeShape
Success{"id": "...", "url": "..."}
Missing id{"error": "missing_id", "message": "..."}
Read-only field violation{"error": "read_only_field_violation", "message": "...", "violations": ["field", ...], "id": "..."}
CRM API error{"code": "...", ...}
Tool error{"error": "invalid_input" | "permission_denied" | "api_error" | "server_error", "message": "..."}
delete_records — Delete Records write

Deletes records by ID from a CRM entity. This is a soft delete: records are flagged as deleted, not physically removed. Related records can be affected — deleting a recurring Appointment also deletes its occurrences, deleting an Email also deletes its draft replies, and deleting an Account re-parents its child accounts. Only entities whose delete permission is true in meta_list_entity_types can be deleted; at most 100 IDs per call.

Parameters

NameTypeRequiredDescription
entity_name*stringRequiredTarget entity.
record_ids*string[]RequiredList of record UUIDs to delete.
Returns:{"results": [...]} — one entry per input ID, in the same order. Failing IDs are skipped; the remaining records are still deleted.
OutcomeShape
Success{"id": "..."}
Invalid id (not a UUID){"error": "invalid_id", "message": "...", "id": "..."}
CRM API error (e.g. not found, no permission){"code": "...", ...}
Tool error{"error": "invalid_input" | "permission_denied" | "api_error" | "server_error", "message": "..."}
upload_cloud_object_document — Upload Cloud Object Document write

Initiates a two-phase file upload into a CRM CloudObjectFolder. This tool is phase 1: it registers the upload (filename, MIME type, target folder, visibility) and returns a short-lived, single-use upload_url. Phase 2 is performed by the client outside the MCP session — it POSTs the file binary as multipart/form-data (field name file) to that URL, e.g. curl -X POST {upload_url} -F file=@/path/to/file. The upload endpoint creates the CloudObject in the CRM and returns it. See the File Upload reference for the full flow, and use search_records on CloudObjectFolder to find a valid folder_id.

Parameters

NameTypeRequiredDescription
document*objectRequiredUpload descriptor with the fields below.
FieldTypeRequiredDescription
filenamestringyesFile name including extension, e.g. report.pdf.
mime_typestringyesMIME type, e.g. application/pdf, image/png.
folder_idstringyesUUID of the target CloudObjectFolder.
is_publicboolnoWhether the file gets a public URL. Default true.
Returns:{"cloud_object_id": "...", "upload_token": "...", "upload_url": "...", "expires_in_seconds": 300, "max_file_bytes": ..., "max_file_mib": ...} — cloud_object_id is a tracking id for the upload (not yet a CRM record id). The client must POST the file to upload_url before expires_in_seconds elapses; the token is consumed on first use. max_file_bytes / max_file_mib is the server's current size limit (upload_max_bytes, see the File Upload reference for this server's value); check the file against it before POSTing — a larger file gets HTTP 413 and the single-use token is spent. The limit is returned at runtime rather than written into the tool description because clients snapshot tool contracts and the value differs per environment. The POST returns the created CloudObject: {"createCloudObject": {"cloudObject": {"id", "filename", "mimeType", "size", "url", "publicUrl", "isPublic", "created"}}}. Returns {"error": "Upload store is not configured"} when the server has no Redis upload store.
bulk_move_opportunities — Bulk Move Opportunities write

Moves a set of opportunities to a new pipeline step. This is a business operation, not a plain field update — it triggers server-side logic: timeframe-in-step tracking, approval removal, optional memo creation, activity completion, and quote status transitions. Cross-pipeline moves are not allowed. Batches of 20 or more are processed asynchronously; the response will include an asyncOperationId to track progress.

Parameters

NameTypeRequiredDescription
opportunity_ids*string[]RequiredUUIDs of the opportunities to move.
step_id*stringRequiredUUID of the destination pipeline step (must belong to the same pipeline).
memo_textstring|nullOptionalOptional memo text linked to each opportunity after the move.
complete_activity_idsstring[]|nullOptionalUUIDs of activities to mark as completed on each opportunity.
accept_quote_idsstring[]|nullOptionalUUIDs of quotes to transition to Accepted status.
archive_quote_idsstring[]|nullOptionalUUIDs of quotes to transition to Lost status.
rollback_methodstringOptionalROLLBACK_ALL (default) — abort the entire batch on any failure. ROLLBACK_FAILED — commit successful records, roll back only failures.
Returns:JSON result object. For async batches: {asyncOperationId: "..."}.
bulk_archive_opportunities — Bulk Archive Opportunities write

Archives a set of opportunities. This is a business operation, not a plain flag update — it triggers server-side logic: AI fitness indicator snapshot, approval removal, optional memo creation, activity completion, and quote archiving (linked quotes transition to Lost). If an opportunity has a recurrence configured with a Lost condition, archiving may spawn a new copy at the recurrence step. Batches of 20 or more are processed asynchronously.

Parameters

NameTypeRequiredDescription
opportunity_ids*string[]RequiredUUIDs of the opportunities to archive.
entity_fieldsobject|nullOptionalOptional field values to apply before archiving (e.g. {"reason_of_close_id": "...", "closing_date": "2024-12-31"}).
memo_textstring|nullOptionalOptional memo text linked to each opportunity after archiving.
complete_activity_idsstring[]|nullOptionalUUIDs of activities to mark as completed.
archive_quote_idsstring[]|nullOptionalUUIDs of quotes to transition to Lost status.
rollback_methodstringOptionalROLLBACK_ALL (default) or ROLLBACK_FAILED.
Returns:JSON result object. For async batches: {asyncOperationId: "..."}.
bulk_archive_leads — Bulk Archive Leads write

Archives a set of leads. This is a business operation, not a plain flag update — it triggers server-side logic: AI fitness indicator snapshot, timeframe-in-step tracking, approval removal, optional memo creation, and task completion. When entity_fields is omitted, all field validation is skipped entirely. Activity completion is scoped to Tasks only; appointments are not affected. Batches of 20 or more are processed asynchronously.

Parameters

NameTypeRequiredDescription
lead_ids*string[]RequiredUUIDs of the leads to archive.
entity_fieldsobject|nullOptionalOptional field values to apply before archiving (e.g. {"reason_of_close_id": "..."}). When omitted, all field validation is skipped.
memo_textstring|nullOptionalOptional memo text linked to each lead after archiving.
complete_activity_idsstring[]|nullOptionalUUIDs of Tasks (only) to mark as completed.
rollback_methodstringOptionalROLLBACK_ALL (default) or ROLLBACK_FAILED.
Returns:JSON result object. For async batches: {asyncOperationId: "..."}.
bulk_qualify_leads — Bulk Qualify Leads write

Converts a set of leads into opportunities. This is a destructive entity-type promotion — each Lead is physically deleted and replaced by a new Opportunity sharing the same primary key. Common fields are copied; the target step must belong to an Opportunity pipeline (steps with lead_process_id are invalid targets). The final (Won) step is also rejected. Both the Lead's step checklist and the target Opportunity step's checklist are enforced. All leads are converted synchronously regardless of batch size.

Parameters

NameTypeRequiredDescription
lead_ids*string[]RequiredUUIDs of the leads to qualify (convert to opportunities).
step_id*stringRequiredUUID of the target Opportunity pipeline step (must have pipeline_id, not lead_process_id; must not be the Won step).
opportunity_fieldsobject|nullOptionalAdditional camelCase field name→value pairs for the resulting opportunities (e.g. {"closingDate": "2026-12-31"}). Keys must use camelCase GraphQL names, not snake_case. Do not include stepId here.
rollback_methodstring|nullOptionalROLLBACK_ALL or ROLLBACK_FAILED. The mutation has no server-side default — omit to let the server decide.
Returns:JSON result object with per-lead qualification outcomes.
change_ownership — Change Ownership write

Unified ownership reassignment across supported CRM entity types. Dispatches to a dedicated GraphQL mutation for Task and Opportunity (processed sequentially, one call per record), and to a plain owner_id field update for Contact, Account, Appointment, Lead, and Memo. Lead and Memo: after reassignment the service account loses write and ownership permissions on those records and cannot self-revert — each successful result includes a warning field. All other entity types return an unsupported error without hitting the CRM.

Parameters

NameTypeRequiredDescription
entity_name*stringRequiredCRM entity type to reassign. Supported: Task, Opportunity, Contact, Account, Appointment, Lead, Memo.
record_ids*string[]RequiredUUIDs of the records to reassign. Must not be empty.
owner_id*stringRequiredUUID of the new owner (Client). Must be a member of the target unit.
unit_idstring|nullOptionalUUID of the new sales unit. Required for Task and Opportunity; optional for other entity types.
optionsobject|nullOptionalEntity-specific flags forwarded to the mutation. Unknown keys are silently ignored; missing keys default to false. Supported keys: keep_owner_as_editor (Task, Opportunity) — keep the displaced owner as a sharing Editor; transfer_linked_records (Opportunity) — cascade-reassign linked records owned by the displaced owner.
Returns:JSON array — one entry per input record, in order.
OutcomeShape
Success{"id": "...", "url": "..."}
Success with warning (Lead / Memo){"id": "...", "url": "...", "warning": "..."}
Per-record mutation error (Task / Opportunity){"id": "...", "error": "..."}
Tool error{"error": "invalid_input" | "permission_denied" | "api_error" | "server_error", "message": "..."}
get_step_checklist_status — Get Step Checklist Status read-only

Returns the checklist items configured on a pipeline step — the requirements that must be satisfied before an opportunity can advance to that step. When opportunity_id is provided, each item is annotated with is_complete (true/false) showing whether the requirement is already satisfied for that specific opportunity. Results are ordered by sort_order ascending.

Parameters

NameTypeRequiredDescription
step_id*stringRequiredUUID of the pipeline step to inspect.
opportunity_idstring|nullOptionalUUID of a specific opportunity. When provided, each item is annotated with its completion status for that opportunity.
Returns:JSON array of checklist item definitions. Each item includes: id, item_type (FieldType / ActivityType / CreateNote / AttachDocument / UseDocumentTemplate / SendEmail / EnrollToEmailSequence / RunProcess / Default), item_validation (Required / RelatedActivityLinked / RelatedActivityCompleted), sort_order, and — when opportunity_id was supplied — is_complete.
complete_step_checklist_item — Complete Step Checklist Item write

Records that a step checklist requirement has been satisfied for a specific opportunity, lead, or quote by creating a LeadOpptyStepCheckListRelation record. Each checklist item type requires different evidence — supply the matching ID parameter. FieldType items do not need a relation record; set the field value directly via update_records instead. Either lead_oppty_id or quote_id must be provided, but not both.

Parameters

NameTypeRequiredDescription
step_checklist_id*stringRequiredUUID of the StepChecklist item being satisfied.
lead_oppty_idstring|nullOptionalUUID of the Opportunity or Lead. Mutually exclusive with quote_id.
quote_idstring|nullOptionalUUID of the Quote. Mutually exclusive with lead_oppty_id.
item_checkedbool|nullOptionalSet to true for Default-type items that have no linked evidence.
activity_idstring|nullOptionalUUID of the Task or Appointment that satisfies an ActivityType item.
note_idstring|nullOptionalUUID of the Note that satisfies a CreateNote item.
cloud_object_idstring|nullOptionalUUID of the uploaded document for AttachDocument / UseDocumentTemplate items.
email_idstring|nullOptionalUUID of the sent Email for SendEmail items.
Returns:JSON result object confirming the created LeadOpptyStepCheckListRelation record.

Tool Annotations

Every MCP tool declares four behavior hints so that AI clients (and their safety layers) can describe a tool's impact before it is called. Each hint is set explicitly — never null — and justified against the tool's actual behavior. The values below are the single source of truth: they are served verbatim in each tool's MCP definition.

HintMeaning
Read OnlyreadOnlyHint — the tool only reads or computes; it never creates, updates, deletes, or sends data.
DestructivedestructiveHint — the tool can delete, overwrite, or cause irreversible side effects. Only meaningful for writes.
IdempotentidempotentHint — calling again with the same arguments yields the same end state and no additional side effects.
Open WorldopenWorldHint — true only if the tool can reach arbitrary external URLs, files, or resources; false when it affects a bounded target (e.g. a record by id in this CRM tenant).
me read-only
HintValueJustification
Read OnlytrueOnly reads the current session's identity claims from the validated Bearer token; makes no CRM API call and writes nothing.
DestructivefalseWrites nothing, so nothing can be overwritten or deleted.
IdempotenttrueReturns the same identity for the same session on every call, with no side effects.
Open WorldfalseReturns identity claims for the caller's own Coevera CRM session only; no external resources are contacted.
meta_list_entity_types read-only
HintValueJustification
Read OnlytrueFetches entity metadata and permissions from the CRM; writes or modifies nothing.
DestructivefalseRead-only call; nothing is overwritten or deleted.
IdempotenttrueRepeated calls return the same metadata and cause no state change.
Open WorldfalseReads only from the caller's own Coevera CRM tenant; it never touches arbitrary external URLs, files, or resources — a closed, bounded domain.
meta_describe_entity read-only
HintValueJustification
Read OnlytrueFetches an entity's field schema from the CRM; writes or modifies nothing.
DestructivefalseRead-only call; nothing is overwritten or deleted.
IdempotenttrueThe same entity returns the same schema on every call, with no side effects.
Open WorldfalseReads only from the caller's own Coevera CRM tenant; it never touches arbitrary external URLs, files, or resources — a closed, bounded domain.
search_records read-only
HintValueJustification
Read OnlytrueQueries records from the CRM; writes or modifies nothing.
DestructivefalseQuery only; nothing is overwritten or deleted.
IdempotenttrueRe-running the same query causes no state change.
Open WorldfalseReads only from the caller's own Coevera CRM tenant; it never touches arbitrary external URLs, files, or resources — a closed, bounded domain.
get_records read-only
HintValueJustification
Read OnlytrueFetches records by id from the CRM; writes or modifies nothing.
DestructivefalseRead-only call; nothing is overwritten or deleted.
IdempotenttrueRepeated fetches of the same ids cause no state change.
Open WorldfalseReads only from the caller's own Coevera CRM tenant; it never touches arbitrary external URLs, files, or resources — a closed, bounded domain.
create_records write
HintValueJustification
Read OnlyfalseWrites new records to the CRM.
DestructivefalsePurely additive — creates new records without overwriting or deleting existing data.
IdempotentfalseEach call inserts new records; calling again with the same input creates duplicates.
Open WorldfalseAffects only records within the caller's own Coevera CRM tenant, addressed by id; it never writes to arbitrary external URLs, files, or resources — a closed, bounded domain.
update_records write
HintValueJustification
Read OnlyfalseModifies existing field values on records in the CRM.
DestructivetrueOverwrites existing field values in place; the previous values are permanently replaced.
IdempotenttrueApplying the same field values again yields the same end state, with no further change.
Open WorldfalseAffects only records within the caller's own Coevera CRM tenant, addressed by id; it never writes to arbitrary external URLs, files, or resources — a closed, bounded domain.
delete_records write
HintValueJustification
Read OnlyfalseDeletes existing records in the CRM.
DestructivetrueRemoves records from the CRM (soft delete — flagged as deleted) and cascades to related records, e.g. recurring appointment occurrences and draft email replies are deleted too.
IdempotenttrueDeleting the same ids again leaves the same end state; already-deleted ids are simply reported as not found.
Open WorldfalseAffects only records within the caller's own Coevera CRM tenant, addressed by id; it never writes to arbitrary external URLs, files, or resources — a closed, bounded domain.
upload_cloud_object_document write
HintValueJustification
Read OnlyfalsePhase 1 of a two-phase upload: writes a one-time upload token to Redis and, once the client POSTs the file to the returned upload_url, a new CloudObject document is created in the CRM.
DestructivefalsePurely additive — a new CloudObject is created inside the target folder; no existing document, record, or field is overwritten or deleted.
IdempotentfalseEach call mints a fresh single-use token; completing the upload twice creates two separate CloudObject documents.
Open WorldtrueUnlike the other tools, the upload is completed by an out-of-band HTTP POST from the client's own environment (e.g. curl in a sandbox), so the tool reaches beyond the bounded MCP session into the client's local files and network.
bulk_move_opportunities write
HintValueJustification
Read OnlyfalseMoves opportunities to a new pipeline step.
DestructivetrueOverwrites step state and cascades irreversible side effects: activities completed and quote statuses transitioned (Accepted/Lost). A pending approval blocks the move rather than being removed.
IdempotentfalseSide effects such as memo creation and activity completion re-run on each call, so it is not a no-op.
Open WorldfalseAffects only records within the caller's own Coevera CRM tenant, addressed by id; it never writes to arbitrary external URLs, files, or resources — a closed, bounded domain.
bulk_archive_opportunities write
HintValueJustification
Read OnlyfalseArchives opportunities.
DestructivetrueRemoves opportunities from active state, transitions linked quotes to Lost, and may spawn a new recurrence copy — irreversible side effects.
IdempotentfalseRepeating can create additional memos/recurrence copies and re-run side effects, so it is not a no-op.
Open WorldfalseAffects only records within the caller's own Coevera CRM tenant, addressed by id; it never writes to arbitrary external URLs, files, or resources — a closed, bounded domain.
bulk_archive_leads write
HintValueJustification
Read OnlyfalseArchives leads.
DestructivetrueRemoves leads from active state and completes linked tasks, overwriting prior state.
IdempotentfalseSide effects (memo creation, task completion) re-run on each call, so it is not a no-op.
Open WorldfalseAffects only records within the caller's own Coevera CRM tenant, addressed by id; it never writes to arbitrary external URLs, files, or resources — a closed, bounded domain.
bulk_qualify_leads write
HintValueJustification
Read OnlyfalseConverts leads into opportunities.
DestructivetrueEach lead is physically deleted and replaced by a new opportunity sharing its primary key; the source lead is permanently removed.
IdempotentfalseAfter the first call the source lead no longer exists, so a repeat call cannot reproduce the same effect.
Open WorldfalseAffects only records within the caller's own Coevera CRM tenant, addressed by id; it never writes to arbitrary external URLs, files, or resources — a closed, bounded domain.
change_ownership write
HintValueJustification
Read OnlyfalseReassigns the owner (and optionally sales unit) on existing records.
DestructivetrueOverwrites the existing owner in place; the change can be irreversible — for Lead/Memo the service account loses write/ownership access after transfer and cannot self-revert.
IdempotentfalseTriggers per-record side effects, and for Lead/Memo the caller can lose access after the first call, so repeating is not guaranteed to be a no-op.
Open WorldfalseAffects only records within the caller's own Coevera CRM tenant, addressed by id; it never writes to arbitrary external URLs, files, or resources — a closed, bounded domain.
get_step_checklist_status read-only
HintValueJustification
Read OnlytrueFetches step-checklist definitions and per-record completion status from the CRM; writes nothing.
DestructivefalseRead-only call; nothing is overwritten or deleted.
IdempotenttrueRepeated calls return the same status with no side effects.
Open WorldfalseReads only from the caller's own Coevera CRM tenant; it never touches arbitrary external URLs, files, or resources — a closed, bounded domain.
complete_step_checklist_item write
HintValueJustification
Read OnlyfalseWrites a new checklist-relation record marking a step requirement satisfied.
DestructivefalsePurely additive — creates a new LeadOpptyStepCheckListRelation; no existing record or field is overwritten or deleted (same class as create_records).
IdempotentfalseEach call can create an additional relation record; repeating is not a no-op.
Open WorldfalseAffects only records within the caller's own Coevera CRM tenant, addressed by id; it never writes to arbitrary external URLs, files, or resources — a closed, bounded domain.

Expand Fields Reference

The expand parameter on search_records and get_records loads related entities inline with each record instead of returning them as URL references. Use meta_describe_entity to find which relation fields support expansion (look for the expandable_fields array in the response).

Forms

FormKeyValueResult
Flat "relation_field" ["sub", "nested.sub", ...] Expands the relation; only the listed sub-fields are returned. Dot-notation in a sub-field also expands that nested object.
Dotted "relation_field.nested" ["sub", ...] Explicitly expands a nested relation inside the parent and returns only the listed sub-fields from it.

Values must be non-empty lists — an empty list is rejected. Both forms can be combined in the same expand object.

Example — flat (single relation)

Returns only account_id and the nested account object (with id and name) for each item in account_relations.

{
  "account_relations": ["account_id", "account.id", "account.name"]
}

Example — flat + dotted (nested relation)

Expand product_relations with explicit sub-fields, and also expand the nested product object returning only name and id.

{
  "product_relations": ["id", "opportunity_id", "product_id"],
  "product_relations.product": ["name", "id"]
}
Discovery: call meta_describe_entity and check the expandable_fields array to see which relation fields support expansion. This includes both built-in relations (e.g. account_relations, primary_contact) and custom lookup fields created in the CRM UI (e.g. cf_lookup_leadoppty_contact). When a record has no related records for an expanded field, the response still includes that field with an empty list — the absence is explicit, not silently omitted.

Filter DSL Reference

The filter parameter for search_records is a nested boolean tree. Leaf nodes map a field api_name to a comparator object.

Logical operators

OperatorDescription
ANDAll conditions must be true
ORAt least one condition must be true
NOTNegates a single condition

Leaf comparators

ComparatorValue typeDescription
String operators — value is a list of strings (multiple values are evaluated as OR)
eq["val", ...]Exact match, case-sensitive
ieq["val", ...]Exact match, case-insensitive
contains["val", ...]Substring match, case-sensitive
icontains["val", ...]Substring match, case-insensitive
starts["val", ...]Prefix match, case-sensitive
istarts["val", ...]Prefix match, case-insensitive
ends["val", ...]Suffix match, case-sensitive
iends["val", ...]Suffix match, case-insensitive
Numeric / date operators — value is a single string
lt"value"Less than
lte"value"Less than or equal
gt"value"Greater than
gte"value"Greater than or equal
Existence operators — value is a boolean (true or false, not a list)
nulltrue / falsetrue — field value is NULL; false — field value is not NULL
emptytrue / falsetrue — field value is an empty string; false — field value is not empty

Example

{
  "AND": [
    {"name": {"contains": ["Acme"]}},
    {"value": {"gte": "10000"}},
    {"OR": [
      {"status": {"eq": ["active"]}},
      {"close_date": {"null": true}}
    ]}
  ]
}

Only fields with filterable: true (returned by meta_describe_entity) can be used in filter expressions. Filterable types: string, int, date, datetime, uuid, enum.

Relation fields are not filterable. Fields listed in expandable_fields (from meta_describe_entity) hold related records, not scalar values, and cannot be used as filter keys — neither the field itself (e.g. account_relations) nor a dot-path into it (e.g. account_relations.account_id). The API silently ignores such conditions; an error is returned if they are detected. Use scalar fields of the main entity instead.

File Upload Reference

MCP tool calls carry JSON, which makes them a poor fit for large binaries (base64 inflates the payload by a third and the whole file would have to pass through the LLM context). Uploads are therefore split into two phases: the MCP tool only registers the upload, and the file itself is sent by the client directly to a dedicated HTTP endpoint.

PhaseWhoWhat happens
1 — register AI client via MCP Calls upload_cloud_object_document with filename, mime_type, folder_id, is_public. The server stores this metadata together with the caller's bearer token (Fernet-encrypted) in Redis under a random single-use token with a 300-second TTL, and returns upload_url + upload_token together with the current size limit (max_file_bytes, currently 52,428,800 bytes = 50 MiB on this server).
2 — upload Client, plain HTTP POST {upload_url} as multipart/form-data with the binary in field file. No Authorization header is needed or read — the token in the path is the credential. The server atomically consumes the token (Redis GETDEL), re-creates a CRM session from the stored bearer token and space, and calls the createCloudObject GraphQL mutation. The created CloudObject is returned as JSON.

Client-side network allow-list. Phase 2 is an outbound HTTP request made from the AI client's own environment (its code-execution sandbox or connector runtime), not through MCP. Claude and ChatGPT restrict which hosts that environment may reach, so the domain *.coevera.com must be allowed in the client application's security / network settings before uploads work — see the app's preferences. If phase 1 succeeds but phase 2 never reaches the server (no POST /upload/… in the logs), this is the first thing to check.

Example

// Phase 1 — MCP tool call
upload_cloud_object_document({
  "document": {
    "filename":  "contract.pdf",
    "mime_type": "application/pdf",
    "folder_id": "0195d175-7d96-e9c2-40fb-d0c793886f92",
    "is_public": false
  }
})
// → {"cloud_object_id": "…", "upload_token": "…", "upload_url": "https://mcp-server.coevera.com/upload/…",
//    "expires_in_seconds": 300, "max_file_bytes": 52428800, "max_file_mib": 50}

// Phase 2 — plain HTTP, from the client's environment
curl -X POST "https://mcp-server.coevera.com/upload/<upload_token>" -F file=@contract.pdf
// → {"createCloudObject": {"cloudObject": {"id": "…", "filename": "contract.pdf", "size": 48213, "url": "…", "publicUrl": null, …}}}

Upload endpoint responses

StatusMeaning
200File stored; body is the createCloudObject payload.
400The multipart body could not be read (client aborted, malformed request). Token is consumed.
410Token expired or already used. Call the tool again to get a fresh one.
413File larger than upload_max_bytes (50 MiB on this server). Token is consumed — compare the file against max_file_bytes from phase 1 before POSTing.
500CRM rejected the upload (e.g. invalid folder_id, no write permission on documents). Body contains the CRM error.
503Either the upload store is not configured on this server (see below), or all upload slots are busy — then a Retry-After header is set and the token is not consumed, so the same upload_url can simply be retried.

Security properties

  • The token is 256 bits of secrets.token_hex randomness — unguessable — and is deleted on first use, so a leaked URL cannot be replayed.
  • The bearer token is encrypted at rest in Redis (upload_token_encryption_key); a Redis dump does not yield live CRM credentials.
  • Phase 2 runs under the original caller's identity and space, so CRM permissions and tenant isolation apply exactly as for any other tool.
  • /upload/* is exempt from BearerAuthMiddleware by design — the one-time token replaces the bearer header for that single request.

Server configuration

The tool is only registered as usable when a Redis upload store is configured. All keys live in config/defaults.py (override via environment / config/local.yml):

KeyPurpose
upload_redis_urlRedis URL for the token store. Unset = feature disabled (tool returns an error, endpoint returns 503).
upload_redis_username / upload_redis_passwordOptional Redis ACL credentials.
upload_token_encryption_keyFernet key used to encrypt the stored bearer token. Required when upload_redis_url is set — startup fails otherwise. Generate with python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())".
upload_token_ttl_secondsHow long the client has to complete phase 2. Default 300.
mcp_server_hostPublic base URL of this server, used to build upload_url. Must be reachable from the client that performs phase 2.
upload_max_bytesHard limit on a single uploaded file; larger bodies get HTTP 413. Returned to the client as max_file_bytes. Currently 52,428,800 (50 MiB).
upload_read_chunk_bytesChunk size used while reading the uploaded body. Currently 1,048,576.
upload_max_concurrentUploads processed at the same time per uvicorn worker. Currently 2.
upload_queue_timeout_secondsHow long a request waits for a free upload slot before getting HTTP 503 + Retry-After. Currently 60.

Entities

Primary Entities

Entity NameLabelCollectionPermissionsDescription
AccountAccountAccountsreadwriteupdatedeleteCoevera Account entity
ContactContactContactsreadwriteupdatedeleteCoevera Contact entity
LeadLeadLeadsreadwriteupdateCoevera Lead entity
TaskTaskTasksreadwriteupdatedeleteTasks are actionable items that can be associated with various entities such as Accounts, Contacts, Leads, Opportunities, etc. They help users manage their activities and follow-ups effectively.Task can by various types - call, email, task - see TaskType
AppointmentAppointmentAppointmentsreadwriteupdatedeleteCoevera Appointment entity
EmailEmailEmailsreadwriteupdatedeleteCoevera Email entity
MemoMemoMemosreadwriteupdatedeleteCoevera Memo entity
MemoCommentMemo CommentMemoCommentsreadwriteupdatedeleteCoevera Memo comments entity
OpportunityOpportunityOpportunitiesreadwriteupdateCoevera Opportunity entity
ProductProductProductsreadwriteupdatedeleteCoevera Product entity
CloudObjectCloudObjectCloudObjectsreadwriteupdatedeleteCoevera CloudObject entity
CloudObjectFolderCloudObjectFolderCloudObjectFoldersreadwriteupdateCoevera CloudObjectFolder entity
FieldFieldFieldsreadCoevera Field entity
PipelinePipelinePipelinesreadCoevera Pipeline entity
StepStepStepsreadCoevera Step entity
ProjectProjectProjectsreadCoevera Project entity
CurrencyCurrencyCurrenciesreadCoevera Currency entity
TaskTypeTaskTypeTaskTypesreadCoevera TaskType entity
StepChecklistStepChecklistStepChecklistsreadChecklist item defining a requirement that must be satisfied at a pipeline step before an opportunity can advance
ClientClientClientsreadCoevera Client entity representing user of the CRM system, used for authentication and ownership of records
SalesUnitSalesUnitSalesUnitsreadCoevera SalesUnit entity representing a sales unit in the CRM system, used for ownership of records

Relation Entities

Entity NameLabelCollectionPermissionsDescription
ContactAccountRelationContact-Account RelationContactAccountRelationsreadwriteupdatedeleteCoevera Contact-Account Relation entity
LeadOpptyAccountRelationLead/Opportunity-Account RelationLeadOpptyAccountRelationsreadwriteupdatedeleteCoevera Lead/Opportunity-Account Relation entity
LeadOpptyContactRelationLead/Opportunity-Contact RelationLeadOpptyContactRelationsreadwriteupdatedeleteCoevera Lead/Opportunity-Contact Relation entity
OpptyProductRelationOpportunity-Product RelationOpptyProductRelationsreadwriteupdatedeleteCoevera Opportunity-Product Relation entity
ProductPipelineRelationProduct-Pipeline RelationProductPipelineRelationsreadwriteupdatedeleteCoevera Product-Pipeline Relation entity
AppointmentContactInviteesRelationAppointment-Contact Invitees RelationAppointmentContactInviteesRelationsreadwriteupdatedeleteCoevera Appointment-Contact Invitees Relation entity
MessageRelationMessage RelationsMessageRelationsreadwriteupdatedeleteCoevera Message Relation entity
CloudObjectRelationCloudObject relationCloudObjectRelationsreadwriteupdatedeleteCoevera CloudObject Relation entity
ActivityRelationActivityRelation - multiple relation typeActivityRelationsreadwriteupdatedeleteCoevera Activity Relation entity between various Task/Appointment and Account/Contact/Lead/Opportunity/Custom entities
SalesUnitClientRelationSalesUnit-Client RelationSalesUnitClientRelationsreadCoevera SalesUnit-Client Relation entity
Field schemas are fetched dynamically per tenant. Use the meta_describe_entity tool (or call meta_list_entity_types first to discover custom entities prefixed with CE_).

Troubleshooting

Common errors

StatusCauseFix
401 UnauthorizedMissing, expired, or malformed Bearer token.Re-authorize via the OAuth flow. Verify the token has not expired and is sent in the Authorization: Bearer <token> header.
403 ForbiddenToken is valid but does not include the required scopes.Re-authorize requesting all scopes listed in the Auth section above.
422 Unprocessable EntityInvalid tool input — bad filter syntax, unknown field, or missing required parameter.Check the Filter DSL reference. Use meta_describe_entity to confirm field api_name values and verify filterable: true before filtering on a field.
429 Too Many RequestsRate limit exceeded (burst or sustained window).Back off and retry. Check the Retry-After response header for the recommended wait time.
406 Not AcceptableClient did not send a supported Accept header. MCP requires at least application/json or text/event-stream.All MCP-compliant clients (Claude, ChatGPT, Python SDK) send the correct Accept header automatically. Raw HTTP clients must include Accept: application/json, text/event-stream.
500 Internal Server ErrorUnexpected server-side error.Check server logs. If the error persists, report it with the failing request details.
503 Service UnavailableCoevera CRM API is unreachable or returned an unexpected response.Retry with exponential back-off. The server retries CRM calls up to 5 times internally before surfacing this error.

Rate limiting

The server uses a two-stage rate limiter: a short-window burst limit per token and a longer-window sustained limit per space (tenant). When either limit is hit, the server returns 429 Too Many Requests.

  • Check the Retry-After response header for the recommended back-off duration.
  • The server retries Coevera CRM API calls internally (up to 5 times, 30 s apart) before surfacing a 503.
  • Prefer batching: pass multiple IDs to get_records rather than making one call per record.

Debug tips

  • Start with meta_list_entity_types — if that fails, the problem is auth or connectivity, not filter syntax.
  • Use meta_describe_entity to confirm exact api_name values before passing fields to search_records or get_records.
  • Only fields with filterable: true (returned by meta_describe_entity) can appear in filter expressions.
  • The /lbready endpoint is public and returns 200 OK when the server is running — use it to confirm reachability before debugging authentication.
  • OAuth discovery documents at /.well-known/oauth-authorization-server and /.well-known/openid-configuration are also public and can confirm the correct token / introspection endpoints.

Release Notes

v2.0.9 October 2026 current
Improved
  • Various minor improvements and bug fixes.
v2.0.8 September 2026
New
  • delete_records — deletes records by ID. This is a soft delete: records are flagged as deleted in the CRM, not physically removed. Results are returned per record in input order, and a failing ID does not block the rest. At most 100 IDs per call; IDs must be UUIDs.
  • Delete permission for Account, Contact, Task, Appointment, Email, Memo, Memo Comment, Product, CloudObject and the relation entities (Contact-Account, Lead/Opportunity-Account, Lead/Opportunity-Contact, Opportunity-Product, Product-Pipeline, Appointment invitees, Message, CloudObject and Activity relations). Lead and Opportunity stay non-deletable — use the archive tools instead.
  • CloudObject and CloudObjectFolder can now be created and updated (previously read-only).
Improved
  • File upload hardening: configurable size limit (upload_max_bytes, default 50 MiB), chunked reads, and a per-worker concurrency gate (upload_max_concurrent, upload_queue_timeout_seconds). upload_cloud_object_document now returns max_file_bytes / max_file_mib so clients can check the size before uploading.
  • OAuth discovery: the authorization-server document now uses this server's origin as issuer (RFC 8414), and advertises PKCE code_challenge_methods_supported: ["S256"], which clients such as ChatGPT require to register a connector.
  • 401 responses now include a WWW-Authenticate challenge with resource_metadata (RFC 9728), so clients can start OAuth discovery from a bare 401.
v2.0.7 September 2026
New
  • upload_cloud_object_document — two-phase file upload into a CRM CloudObjectFolder. The tool registers the upload and returns a single-use, time-limited upload_url; the client then POSTs the file as multipart/form-data to the new /upload/{token} endpoint, which creates the CloudObject under the original caller's identity. See the File Upload reference.
  • Configuration keys upload_redis_url, upload_token_encryption_key, upload_token_ttl_seconds, mcp_server_host. The feature is disabled unless Redis is configured.
v2.0.6 July 2025
Internal
  • Small internal improvements - tool annotations
v2.0.5 July 2025
Internal
  • Small internal improvements
v2.0.4 July 2025
Internal
  • Tools annotations now include justification for each hint, explaining the reasoning behind the true/false value
v2.0.3 July 2025
Internal
  • Fixed complete_step_checklist_item tool annotations
v2.0.2 July 2025
Internal
  • Fixed tools annotations
v2.0.1 July 2025
Internal
  • Fixed Oauth scope mismatch
v2.0.0 June 2025
New
  • change_ownership — unified ownership reassignment tool covering Task, Opportunity, Contact, Account, Appointment, Lead, and Memo in a single call. Dispatches to a dedicated changeOwnership* GraphQL mutation for Task and Opportunity, and to a plain REST owner update for the remaining entity types. Lead and Memo transfers include a warning in the response: the service account loses write/ownership access on those records after reassignment and cannot self-revert.
  • Domain operation tools — bulk_move_opportunities, bulk_archive_opportunities, bulk_archive_leads, and bulk_qualify_leads — execute business operations that trigger server-side logic (timeframe tracking, approval removal, AI fitness snapshots, quote transitions, recurrence spawning). Each tool accepts an optional rollback_method (ROLLBACK_ALL or ROLLBACK_FAILED). Batches of 20 or more are processed asynchronously; the response includes an asyncOperationId. All operations are also instrumented with integration metrics.
  • Step checklist tools — get_step_checklist_status and complete_step_checklist_item — expose the per-step entry requirements configured by pipeline admins. get_step_checklist_status lists all checklist items for a step and, when an opportunity_id is provided, annotates each item with is_complete. complete_step_checklist_item records that a requirement has been satisfied by creating a LeadOpptyStepCheckListRelation record; the correct evidence ID parameter depends on the item type (activity, note, document, email, or a simple item_checked: true for Default items). FieldType items do not use this tool — set the field value directly via update_records instead.
  • Custom lookup fields are now discoverable via meta_describe_entity. CRM fields with primitive_type: list that carry a lookup.entity_type are surfaced in the expandable_fields array alongside built-in relation fields (e.g. account_relations). These fields — typically named cf_lookup_<entity>_<target> — can be passed to the expand parameter of search_records and get_records to load related records inline.
Internal
  • CoeveraEntity now carries an OwnershipConfig that encodes per-entity dispatch method, required fields, and any access-loss warning — making ownership routing data-driven rather than hard-coded.
v1.1.5 June 2025
Improved
  • All tool annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) are now explicitly set to true or false on every tool — no implicit nulls.
v1.1.4 May 2025
New
  • OpenAI Apps challenge endpoint (GET /.well-known/openai-apps-challenge) — enables ChatGPT plugin verification.
  • Token introspection response caching — validated tokens are cached to reduce round-trips to the OAuth server on repeated requests.
Improved
  • Configurable token cache TTL (token_cache_timeout) to control how long introspection results are reused.
  • Error handling hardened across all write tools — invalid inputs, read-only field violations, and CRM API errors are now reported per-record rather than failing the entire batch.
  • Redis rate-limit errors surface a structured 503 response instead of an unhandled exception, keeping the server operational when Redis is degraded.
v1.1.3 April 2025
New
  • SSE (Server-Sent Events) streaming — tool calls send incremental notifications/progress events to the client, giving users live feedback during long-running CRM queries.
  • Added units_and_roles.read to the required OAuth scope set.
Improved
  • get_records no longer applies a sample cap — all matched records are returned regardless of count.
  • Tool output schemas optimised for ChatGPT compatibility (create_records, update_records).
  • Content Security Policy (CSP) headers added to all responses.
  • Tool titles and descriptions revised for clarity across all 7 tools.