# `save_person_notes`

Atomically adds, corrects, or explicitly forgets private notes on one person in your own Rolodex, only when you request it. Saving starts no search and contacts nobody.

Annotations: `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

## Inputs

| Input | Required | Description |
| --- | --- | --- |
| `personId` | yes | Exact owned-person UUID. |
| `expectedVersion` | yes | Current opaque version from `get_person_notes`. |
| `requestId` | yes | New UUID for a new action; retain it and all arguments on an identical retry. |
| `changes` | yes | Between 1 and 12 changes for this person, committed together. |

## Returns

`{ status: "success", outcome, message, requestId, state, changes }`. `outcome` is `saved` or `no_change`. `state` is the current person-note state. Each change receipt has `action`, `noteId`, `text`, and `outcome` (`saved`, `forgotten`, or `unchanged`). A receipt confirms the save, not a search or outreach. Notes remain private and can inform future matching.

## Changes and retries

- Add: `{ "action": "save", "text": "One claim", "tag": "general" }`.
- Correct: use the same save shape plus `replacesNoteId` naming the active note. The old fact becomes superseded.
- Forget: `{ "action": "forget", "noteId": "<active note UUID>" }`. This removes the selected fact without restoring an older correction. Retained history remains visible to you.

Save text must be a single line of 1–280 characters after trimming. Preserve attribution, uncertainty, negation, and timing. Tags are `current_focus`, `preference`, `constraint`, `relationship`, `watch_out`, `comms_style`, or `general` (default). Change each existing note at most once in a batch.

An active duplicate fact stays unchanged, including its existing tag. A previously removed or superseded fact is rejected rather than restored automatically. Repeating an already applied forget returns `no_change`.

A stale version returns `version_conflict` before any changes. Identical request-ID retries return `no_change` only while their original effects still hold, even if unrelated notes were added later. A later correction or removal prevents replay of the earlier save. Reusing an ID with different arguments returns `request_id_conflict`. The request ID is private to the member. After a conflict, review current notes and prepare a new action with a new ID; never retry automatically over the newer state.

## Example

```json save_person_notes
{
  "personId": "00000000-0000-4000-a000-000000000005",
  "expectedVersion": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
  "requestId": "00000000-0000-4000-a000-000000000002",
  "changes": [
    {
      "action": "save",
      "text": "Prefers email",
      "tag": "comms_style"
    }
  ]
}
```

Result (synthetic data):

```json result:save_person_notes
{
  "status": "success",
  "outcome": "saved",
  "message": "Your private note changes were saved.",
  "requestId": "00000000-0000-4000-a000-000000000002",
  "state": {
    "person": {
      "personId": "00000000-0000-4000-a000-000000000005",
      "contactAuthoredName": "Synthetic Pat",
      "ownerAuthoredName": null,
      "emails": [
        "pat@example.test"
      ]
    },
    "version": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
    "notes": [
      {
        "id": "00000000-0000-4000-a000-000000000006",
        "text": "Prefers email",
        "tag": "comms_style",
        "status": "active",
        "createdAt": "2026-10-09T00:00:00.000Z"
      }
    ]
  },
  "changes": [
    {
      "action": "save",
      "noteId": "00000000-0000-4000-a000-000000000006",
      "text": "Prefers email",
      "outcome": "saved"
    }
  ]
}
```

## Related

See the [private notes workflow](/docs/mcp/agent-guide#private-person-notes) and [errors and limits](/docs/mcp/errors-and-limits).
