---
name: contacts
description: "CRM contacts plugin. Provides contact-create, contact-lookup, contact-update, contact-delete, contact-list, contact-export, contact-erase, group-create, and group-manage tools for managing the customer contact graph and group conversations."
tools:
  - name: contact-create
    publicAllowlist: false
    adminAllowlist: false
  - name: contact-lookup
    publicAllowlist: false
    adminAllowlist: false
  - name: contact-update
    publicAllowlist: false
    adminAllowlist: false
  - name: contact-delete
    publicAllowlist: false
    adminAllowlist: false
  - name: contact-list
    publicAllowlist: false
    adminAllowlist: false
  - name: contact-export
    publicAllowlist: false
    adminAllowlist: false
  - name: contact-erase
    publicAllowlist: false
    adminAllowlist: false
  - name: group-create
    publicAllowlist: false
    adminAllowlist: false
  - name: group-manage
    publicAllowlist: false
    adminAllowlist: false
always: false
embed: false
metadata: {"platform":{}}
mcp:
  command: node
  args:
    - ${PLATFORM_ROOT}/lib/mcp-spawn-tee/dist/index.js
    - ${PLATFORM_ROOT}/plugins/contacts/mcp/dist/index.js
  env:
    MCP_SPAWN_TEE_NAME: contacts
    LOG_DIR: ${LOG_DIR}
    PLATFORM_ROOT: ${PLATFORM_ROOT}
    ACCOUNT_ID: ${ACCOUNT_ID}
    SESSION_ID: ${SESSION_ID}
    AGENT_SLUG: ${AGENT_SLUG}
mcp-manifest: auto
---

# Contacts

Manages customer contact records and group conversations in the knowledge graph. Admin agent only — the public agent has no write access.

Tools are available via the `contacts` MCP server.

## Provenance anchoring

`contact-create` writes a `:Person` node, which is one of the action-provenance-gated labels in `writeNodeWithEdges`. The write should carry an inbound `:PRODUCED` edge from `:Task`, `:Conversation`, or `:Message`. There is no agent-visible field for this — the admin server stamps `SESSION_NODE_ID` (the `sessionId` UUID of the active `:AdminConversation`) at MCP spawn time, and the wrapper auto-injects the `:PRODUCED` edge via `injectConversationProvenance`. The helper MATCHes `(c:Conversation {sessionId, accountId})` — account isolation is enforced inside the natural key, not as a separate gate, so a cross-account env-stamp returns zero rows and never injects.

The agent passes whichever organic relationships are appropriate (`PARTICIPATES_IN` to a Conversation, `WORKS_FOR` to an Organization, `KNOWN_VIA` to a referral source) — the provenance edge is composed automatically and is not an agent concern. When the env-stamp resolution fails (loud `[mcp:contacts] [provenance-missing] reason=<...>`), the downstream gate emits a `[graph-write] warn reason=missing-provenance labels=<csv> agent=<agentLabel>` line and the write proceeds (Task 580 relaxed this from a hard reject). The warn carries the labels and agent so operators can quantify how often the gap fires; the upstream `[provenance-missing]` line names the resolution failure that caused it.

See [.docs/neo4j.md § Process provenance doctrine](../../../.docs/neo4j.md) for the doctrine and observability surface.
