update_member_email
Status: Implemented | Module: member
Add or replace an email address on the verified member’s profile. CRITICAL: this tool REPLACES by default — it does not append. The INTENT contract is what keeps a silent overwrite from destroying contact data.
For a one-off send to a different address, do NOT call this tool — pass the email directly to send_confirmation.
INTENT Contract
Section titled “INTENT Contract”When the member already has at least one email AND the new value does not exact-match any record AND no intent arg was passed, the tool returns INTENT_REQUIRED with the current values + the new value in the message. STOP and ASK the member how to proceed, then re-call with one of the supported intents:
| Intent | Effect |
|---|---|
replace |
Overwrite an existing primary in place. Pair with replace_email_id to target a specific row; otherwise the current primary (or first row) is chosen. |
add_secondary |
Keep existing primary, save new as is_primary=false. |
add_as_primary |
Demote current primary to secondary; save new as primary. Rolls back demotions on create failure so the member is never left without a primary. |
correct_typo |
Update the single existing record’s value/note in place. ONLY valid when exactly one record exists. |
When NO emails exist, the tool creates the new value as primary regardless of intent. When the new value EXACT-matches (case-insensitive) an existing row, the matched row is updated in place — no INTENT needed.
Annotations
Section titled “Annotations”| Hint | Value |
|---|---|
| readOnlyHint | false |
| destructiveHint | false |
| idempotentHint | false |
| Field | Type | Required | Description |
|---|---|---|---|
session_id |
string | yes | Active session ID from verify_member |
email |
string | yes | Email address to add or update (must contain @ and .) |
intent |
string | conditional | Required when records exist + new value doesn’t match. One of replace, add_secondary, add_as_primary, correct_typo. |
replace_email_id |
string | no | When intent=replace, the specific email_id to overwrite. Get this from get_member_email. |
note |
string | no | Optional label (e.g. Personal, Caretaker email) |
Output
Section titled “Output”| Field | Type | Description |
|---|---|---|
status |
string | success or error |
email_id |
string | Email record UUID |
email |
string | Email address saved |
action |
string | created, updated, replaced, added_secondary, or added_as_primary |
is_primary |
bool | Whether the saved row is the primary |
message |
string | Human summary |
guidance |
Guidance | Suggests send_confirmation as next step |
v2.0 audit change: MemberName is no longer echoed (the caller already has it from the session — AUDIT-3 / P2-5). is_primary was added so the agent can confirm the resulting state to the member.
Side Effects
Section titled “Side Effects”- Writes to the member-contact store via the nemt-objects Email facade.
- Emits a HIPAA audit entry
mcp.update_member_email— metadata carriesaction+intentonly. The raw email value is NEVER logged or audited. - INTENT_REQUIRED rejections also emit an audit row (
outcome=denied,action=intent_required) so refusals are inspectable. - Error logs redact the raw email value.
Error Codes
Section titled “Error Codes”INTENT_REQUIRED— Member has existing records and the new value doesn’t exact-match any of them. Re-call with anintentarg.MISSING_PREREQUISITE— Session missing or invalid email format.
Related
Section titled “Related”- Source:
internal/app/member/usecases/update_member_email.go,internal/app/member/register.go - Read companion: get_member_email (provides the
email_idyou pass asreplace_email_id) - Prerequisite: verify_member
- Next: send_confirmation