Use the MCP as a set of small building blocks. The safe path is to inspect the actual workspace, form a concrete plan, preview each mutation, apply the smallest coherent change, and read the result back.
The protocol uses the crm_records_* names and record_id for compatibility. In Havilo, those records are Entities: people, companies, programs, investments, projects, or any other type in your workspace.
1. Confirm the connection
Call workspace_whoami and workspace_capabilities. Record the workspace ID for the plan even though customer tools will not accept it as input.
Stop if the connected workspace or effective permissions are not the ones the task requires.
2. Inspect the model
Call crm_schema_get before inventing fields or relationship types. It returns the active entity types, their fields with how many values each one holds, relationship types, and Entity counts.
Prefer an existing type or field when it already carries the intended meaning. If the model needs to change, use:
crm_entity_types_ensure;crm_fields_ensure;crm_relationship_types_ensure.
Each tool is idempotent and previews by default. Review the existing match and intended values before applying it.
3. Find the target Entities
Use crm_records_search to search by text, entity type, or external ID. Use crm_records_get when you already know the Entity's record_id and need its fields, relationships, and recent Notes.
External IDs are the best stable identity when data originates in another system. Do not match a large import only by display name when the source provides a durable identifier.
4. Preview the write
crm_records_upsert, crm_relationships_ensure, and crm_notes_upsert all default to apply: false.
A useful plan identifies:
- the Entities and entity types being changed;
- the source of each value;
- stable external IDs or idempotency keys;
- the relationship type and direction;
- the Notes and Entity links being written;
- the read-back calls that will prove the result.
Review the preview for conflicts, inferred matches, and target IDs. Change the input before applying if any target is ambiguous.
4b. Edit a Note precisely
To change part of a Note rather than replace it, read it and then name the text you are changing.
crm_notes_read returns the whole Note as markdown, with custom blocks as ::: fences, plus a revision. crm_notes_edit takes edits, each naming the exact old_string to replace. Copy that string from what the read returned — an edit that does not match exactly once fails rather than guessing which occurrence you meant.
{
"note_id": "a1b2c3",
"revision": "sha256:9f2e…",
"edits": [
{
"old_string": "Pre-seed specialist.",
"new_string": "Pre-seed specialist. Co-invests with Madrona.",
},
],
"apply": true,
}Pass revision back so the edit is refused if the Note changed after you read it, rather than overwriting someone's work. Use append to add to the end, which needs no anchor. Use crm_notes_upsert only when replacing a whole Note.
Editing inside a ::: fence is ordinary text editing — the block survives, because the server confirms it before writing. See Tool reference for the exact failure modes.
5. Apply and verify
Repeat the reviewed input with apply: true. Then read back the affected Entities or Notes.
For a relationship change, inspect both Entities if direction or labels matter. For a Note, confirm the saved markdown and linked Entity IDs. A successful write response confirms that the mutation returned; the verification read confirms what is now in the workspace.
For more than 100 Entities or a source file that should be auditable as one job, use Import data.