Session handoff 2026-06-28: reorder + MCP fix

Two things completed in this session: (1) reorder op implemented and deployed; (2) persistent MCP -32602 bug root-caused and fixed.

1. Reorder op — DONE, deployed, tested

The reorder op is live on both pomelo (local) and gravlax (production). All three interfaces work:

• HTTP: POST or PATCH /{key} with {"op": "reorder", "order": [<id>, ...]}

• MCP tool: reorder(key, order, if_rev?) — delegates to db_patch

• CLI: notes reorder <key> <id> [<id> ...]

Strict validation before any mutation: 422 on missing/unknown/duplicate IDs. Supports if_rev for optimistic concurrency.

Tests: 18 HTTP tests in test_reorder.py, 11 MCP tests in test_mcp_tools.py (TestReorder class). All passing.

Also added PATCH /{key} route as a proper HTTP method (previously only POST did patch ops).

2. MCP -32602 — FIXED (takes effect on next Claude Code session)

Architecture

Claude Code on pomelo reaches the MCP server via stunnel, NOT via Apache:

pomelo:~/.claude.json  →  http://127.0.0.1:8023/mcp/
         ↓ stunnel client (pomelo /etc/stunnel/stunnel.conf)
gravlax:18023
         ↓ stunnel server (gravlax /etc/stunnel/stunnel.conf)
gravlax:127.0.0.1:8023  ← gdata-mcp-server.service

The Apache proxy (https://www.critchley.biz/mcp → gravlax:8023) is used by other clients (e.g. cloud-based Claude, Codex). Apache Streamable HTTP requests show in other_vhosts_access.log; stunnel requests show only in the service journal.

Root cause

~/.claude.json had "type": "sse". The SSE MCP client caches session state. After a server restart (mid-session), it reconnects (new session_id from GET /mcp/) but skips the initialize handshake because its internal state says "already initialized". The server rejects every subsequent tool call with -32602.

Fix

Changed "type": "sse" to "type": "http" (Streamable HTTP) in ~/.claude.json. Streamable HTTP is stateless: every POST to /mcp/ is a self-contained JSON-RPC call — no session, no initialize step, no stale state after restart.

Verified: curl POST to http://127.0.0.1:8023/mcp/ with both initialize and tools/call payloads both return 200 with correct results.

The fix takes effect on the next Claude Code session start. The current session still fails (old SSE connection already established).

Apache config changes (harmless, leave in place)

Added to the <Location /mcp> block in /etc/apache2/sites-enabled/www.critchley.biz-ssl.conf while debugging:

ProxyPass http://127.0.0.1:8023/mcp flushpackets=on disablereuse=On
SetEnv proxy-initial-not-pooled 1
SetEnv no-gzip 1
SetEnv gzip-only-text/html 1

These are defensive and correct for SSE proxying. disablereuse=On prevents stale pooled connections after restarts. no-gzip prevents mod_deflate buffering the stream.

State on gravlax

• gdata-mcp-server.service: running, SSE + Streamable HTTP both enabled (MCP_SSE default true)

• All tests pass locally (python -m pytest test_reorder.py test_mcp_tools.py)

• Code committed and rsync'd to gravlax

Next session: just verify MCP tools work

After restarting Claude Code, run any MCP tool (e.g. mcp__gdata__keys). Should return results without -32602. If it fails, check ~/.claude.json still has "type": "http".

created 2026-06-28