STATUS: DRAFT. This file documents the env-variable changes required to activate the CalendarWrite v1 Authoritative Guard on each MCP instance, per ADR-0002 amendments A1–A15. It is intentionally NOT applied — applying it is substep 2h (human-gated). Read the diff against
pre-calendarwrite-v1first, then apply per the procedure below.
The patched gcalendar/calendar_tools.py is a SHARED Python file consumed by
all three MCP instances (google-personal, google-ema, google-llos).
ADR-0002 A3 mandates that each instance carries its own
CALENDAR_ATTENDEE_POLICY env var; the file does not, and must not, hardcode
which instance is permissive.
Default behavior when env vars are unset is fail-closed: policy degrades
to "disabled" (no attendee writes allowed). This means a missed restart
fails toward "no exfil channel," not "permissive."
| MCP instance | port | CALENDAR_ATTENDEE_POLICY |
CALENDARWRITE_ALLOWLIST_PATH |
|---|---|---|---|
google-personal |
8001 | allowlist |
~/.claude/skills/CalendarWrite/config/allowlist.json |
google-ema |
8002 | disabled |
(unset — env is unused under disabled) |
google-llos |
8003 | disabled |
(unset — env is unused under disabled) |
Rationale:
google-personalis the CalendarWrite domain owner. It needsallowlistpolicy + a path to the seed allowlist (substep 2d-Step-4: Laura + Colin personal,kind: "person"only).google-ema/google-lloshad no attendee writes before this patch, and will have none after.disabledpreserves prior behavior. If/when EMA or LLOS later need attendee writes, each gets its ownallowlistpolicy + its own allowlist file via a new ADR — never via this one.
launchd does not perform shell ~ expansion. The allowlist loader runs
Path(value).expanduser().resolve() (A14), so passing the raw ~/.claude/…
string into the plist is safe. The startup log will print the RESOLVED
absolute path for verification (Step 3 #16).
The MCP instances are started by launchd:
| Instance | launchd label | plist |
|---|---|---|
google-personal |
com.pai.google-personal-mcp |
~/Library/LaunchAgents/com.pai.google-personal-mcp.plist |
google-ema |
com.pai.google-ema-mcp |
~/Library/LaunchAgents/com.pai.google-ema-mcp.plist |
google-llos |
com.pai.google-llos-mcp |
~/Library/LaunchAgents/com.pai.google-llos-mcp.plist |
Each plist already has an EnvironmentVariables dict with PATH and HOME.
Add the two new keys (or one, for ema/llos) inside that same dict. Do NOT
add them to start-mcp.sh's account dispatch — putting them in the plist
keeps the per-instance policy at the OS layer rather than entangling it
with the shared launch script, which keeps the security policy visible to
launchctl print.
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
<key>HOME</key>
<string>/Users/colinmorawski</string>
<!-- CalendarWrite v1 — see ADR-0002 A1-A15 -->
<key>CALENDAR_ATTENDEE_POLICY</key>
<string>allowlist</string>
<key>CALENDARWRITE_ALLOWLIST_PATH</key>
<string>~/.claude/skills/CalendarWrite/config/allowlist.json</string>
</dict><key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
<key>HOME</key>
<string>/Users/colinmorawski</string>
<!-- CalendarWrite v1 — disabled (no attendee writes from this MCP) -->
<key>CALENDAR_ATTENDEE_POLICY</key>
<string>disabled</string>
</dict>Do this AFTER you've reviewed the diff on feat/calendarwrite-v1-attendees-allowlist,
the test suite is green, and (optionally) you've run a final Codex review.
-
Seed the allowlist (Step 4 of BUILD-PLAN):
mkdir -p ~/.claude/skills/CalendarWrite/config # Write allowlist.json with {version: 1, entries: [...]}. # All entries MUST have kind: "person" — group/list/alias entries are # rejected by the loader. chmod 600 ~/.claude/skills/CalendarWrite/config/allowlist.json
-
Patch each plist in-place. Apple's
plutilis the safe tool:# google-personal plutil -insert EnvironmentVariables.CALENDAR_ATTENDEE_POLICY -string 'allowlist' \ ~/Library/LaunchAgents/com.pai.google-personal-mcp.plist plutil -insert EnvironmentVariables.CALENDARWRITE_ALLOWLIST_PATH \ -string '~/.claude/skills/CalendarWrite/config/allowlist.json' \ ~/Library/LaunchAgents/com.pai.google-personal-mcp.plist # google-ema plutil -insert EnvironmentVariables.CALENDAR_ATTENDEE_POLICY -string 'disabled' \ ~/Library/LaunchAgents/com.pai.google-ema-mcp.plist # google-llos plutil -insert EnvironmentVariables.CALENDAR_ATTENDEE_POLICY -string 'disabled' \ ~/Library/LaunchAgents/com.pai.google-llos-mcp.plist
(Use
plutil -replace …if the key already exists.) -
Restart all three instances (ADR-0002 A3 — the shared Python file means ema/llos's next restart for ANY reason will load the patched code, so restarting deterministically now is the only way to know the operative state):
launchctl kickstart -k gui/$(id -u)/com.pai.google-personal-mcp launchctl kickstart -k gui/$(id -u)/com.pai.google-ema-mcp launchctl kickstart -k gui/$(id -u)/com.pai.google-llos-mcp
-
Verify the startup log line for each instance (Step 3 #16):
grep CALENDARWRITE_ALLOWLIST ~/hardened-google-workspace-mcp/mcp_server_debug_8001.log | tail -1 # expect: status=loaded policy=allowlist path=/Users/colinmorawski/.claude/... entries=<N> sha256=<hash> grep CALENDARWRITE_ALLOWLIST ~/hardened-google-workspace-mcp/mcp_server_debug_8002.log | tail -1 # expect: status=loaded policy=disabled path=- entries=0 sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 grep CALENDARWRITE_ALLOWLIST ~/hardened-google-workspace-mcp/mcp_server_debug_8003.log | tail -1 # expect: same as ema
The
google-personallog MUST show an absolute path (no leading~) — that's the A14 sanity check. The empty-allowlist sha256 above is a known constant (sha256 of the empty string).
If anything is wrong after restart, revert to the rollback anchor:
cd ~/hardened-google-workspace-mcp
git diff pre-calendarwrite-v1..HEAD -- gcalendar/ core/ # review
git reset --hard pre-calendarwrite-v1 # only after confirmation
# Optional: undo the plist env-var inserts
plutil -remove EnvironmentVariables.CALENDAR_ATTENDEE_POLICY \
~/Library/LaunchAgents/com.pai.google-personal-mcp.plist
plutil -remove EnvironmentVariables.CALENDARWRITE_ALLOWLIST_PATH \
~/Library/LaunchAgents/com.pai.google-personal-mcp.plist
plutil -remove EnvironmentVariables.CALENDAR_ATTENDEE_POLICY \
~/Library/LaunchAgents/com.pai.google-ema-mcp.plist
plutil -remove EnvironmentVariables.CALENDAR_ATTENDEE_POLICY \
~/Library/LaunchAgents/com.pai.google-llos-mcp.plist
# Restart all three
launchctl kickstart -k gui/$(id -u)/com.pai.google-personal-mcp
launchctl kickstart -k gui/$(id -u)/com.pai.google-ema-mcp
launchctl kickstart -k gui/$(id -u)/com.pai.google-llos-mcpThe pre-CalendarWrite code path rejects all attendees parameter writes
(the original CW-MODIFIED removal), so rolling back returns the system
to "no attendee writes from any MCP instance" — the safest state.