An MCP connection grants access to one workspace. It does not create a second permission system and it does not turn the OAuth scope into blanket authority.
Three checks apply
Every request is constrained by all three of these boundaries:
- Connection profile. The registered customer client can enter only the workspace MCP surface.
- Workspace grant. The grant stores the exact workspace and membership chosen during authorization. An API key is bound to its workspace and acts through its creator's membership.
- Live permissions. The called tool checks the permissions attached to that membership's current role and permission sets. Through an API key, a permission counts only when the key grants it too.
The result is an intersection, not an escalation. A valid token cannot exceed the bound workspace or the person's current Havilo permissions.
The catalog stays stable
The customer server registers a stable tool catalog. Permission changes do not add or remove tools from it. Instead, Havilo evaluates the relevant permission when a tool is called.
For example, someone may still see crm_notes_upsert after NOTE_UPDATE is removed. A subsequent update attempt will fail authorization. workspace_capabilities shows the effective permissions currently available to the connection.
Workspace isolation
Customer tool schemas do not accept workspace_id. Havilo injects the workspace stored on the OAuth grant or API key into every data, AI, import, and job operation.
Entity, relationship, Note, and job identifiers are checked inside that workspace. Knowing an identifier from another workspace does not make it readable through the connection.
If the user, membership, workspace, OAuth client, or grant becomes inactive, authentication fails. If only a granular permission changes, affected tool calls fail while still-authorized calls continue to work.
CRM field visibility
Workspace CRM reads apply the caller's current role restrictions as well as ENTITY_READ.
crm_records_get and crm_records_search omit hidden scalar and list fields, including fields on
linked records. Search does not match against hidden fields, and crm_schema_get omits their
definitions, options, and value counts. This also applies to the in-app agent's CRM tools.
Entity-type, field, and relationship-type previews also filter any returned field definitions.
Record write previews and field suggestions cannot inspect a hidden field's current value. Requests targeting an inaccessible field are rejected before preview or application. Workspace owners and Administrators retain their existing field access; the Name field remains visible.
Preview and apply permissions
Consequential tools use apply: false by default. Preview usually requires read permission so an agent can inspect the proposed target and result. Apply requires the corresponding write permissions and can therefore fail even when preview succeeded.
Examples:
- previewing an Entity upsert requires
ENTITY_READ; - updating Entities by
record_idrequiresENTITY_UPDATE, orENTITY_UPDATE_LINKEDwhen every record is linked to the caller through aUserEntityLink; - filing a field suggestion with
crm_fields_suggestrequiresENTITY_READ; - creating an Entity requires
ENTITY_CREATEandENTITY_UPDATE; - previewing a note upsert requires
NOTE_READ; - creating a note requires
NOTE_CREATEandNOTE_UPDATE; - applying a CSV import requires
BULK_OPERATIONS,ENTITY_CREATE, andENTITY_UPDATE.
Reasoning requires AI_FEATURE_ACCESS. Reach is not a tool argument on this surface — ai_plan and ai_run always work within the connected workspace — so VIEW_GLOBAL_NETWORK is only reachable through the xray and global_search modes of ai_runs_start.
See Tool reference for the exact requirement on every mutation.