Files
workbench-server/.claude/handoffs/2026-09-28-163442-security-fix-and-writeback.md
2026-09-28 16:35:59 -04:00

6.3 KiB

Handoff: workbench-server — path traversal fixed, page write-back added, MCP registered

Session Metadata

  • Created: 2026-09-28 16:34:42
  • Project: /home/claude/bin/workbench-server
  • Branch: master (pushed to git.sethpc.xyz/Seth/workbench-server)
  • Session duration: ~1.5 h (shared with IO-102 work)

Recent Commits (for context)

  • a6766e8 docs: document page write-back via POST /log
  • 05fcb7f feat: POST /log from the page + load state.json on page open
  • 6562250 fix: block path traversal in static handler
  • 4b55135 docs: add open source subheading
  • c97adfc docs: README and INSTALL.md — AI-guided setup with SSH ControlMaster

Handoff Chain

  • Continues from: None (first handoff; earlier history lives in ~/bin/claude_analog/SESSION.md, "Workbench MCP Server" section)
  • Supersedes: None

Current State Summary

The workbench MCP server was already built but had never been used live. This session gave it a debug run: tests passed and scaffold/HTTP/WS/log/stop worked, but the MCP never loaded because it was only listed under mcpServers in ~/.claude/settings.json, which Claude Code ignores. It is now registered at user scope (claude mcp add --scope user workbench -- /home/claude/.local/bin/workbench mcp) and shows ✔ Connected. Along the way I found and fixed a LAN-exposed path traversal (any file readable by claude was servable). I also added POST /log so pages can record data, and made the scaffold load state.json when a page opens. 18/18 tests pass. First real project: ~/workbench/io102 (see ~/bin/claude_analog/.claude/handoffs/).

Codebase Understanding

Architecture Overview

  • src/workbench/server.py: WorkbenchServer (testable core) + create_mcp_server (FastMCP wrapper, 6 tools). Each project gets its own aiohttp app on a port from 8070 up: /ws, POST /log, static files under the project dir.
  • src/workbench/project.py: on-disk layout under ~/workbench/<name>/: index.html (from scaffold), state.json, session.md/.jsonl, cost-log.jsonl, .server.json (pid/port).
  • src/workbench/scaffold.html: WS client. State keys are template (HTML), styles, script (run via new Function). On load: fetch state.json, fall back to localStorage.
  • Editable pip install (~/.local/bin/workbench → this source tree), so source edits apply on the next MCP start.

Critical Files

File Purpose Relevance
src/workbench/server.py HTTP/WS/MCP Static handler now resolves paths and requires is_relative_to(root)
src/workbench/scaffold.html Browser client Log feed renders as text (browsers can now POST entries)
tests/test_server.py Includes live-server tests for traversal and POST /log

Key Patterns Discovered

  • Tests use a tmp_workbench fixture; live-server tests call server._start_http_server directly and use aiohttp's ClientSession.

Work Completed

Tasks Finished

  • Debug run: pytest, a live smoke test, and an MCP stdio handshake (all 6 tools listed)
  • Registered the MCP at user scope (~/.claude.json backed up to ~/.claude/.backup-claude.json.* / ~/.claude/.backup/)
  • Fixed path traversal (commit 6562250), with a test
  • Added POST /log + load-from-state.json + text-only log feed (05fcb7f), with a test
  • README note (a6766e8)

Files Modified

File Changes Rationale
src/workbench/server.py traversal guard; log_handler security; page write-back
src/workbench/scaffold.html fetch state.json on load; text-only log entries new devices see content; no script injection via /log
tests/test_server.py 2 tests
README.md write-back paragraph

Backups: src/workbench/.backup/, tests/.backup/, .backup/README.md.*

Decisions Made

Decision Options Considered Rationale
Browser write-back via POST /log reusing append_log A separate readings store One log for humans and the AI; workbench_read_log already covers resume
Server for io102 was run as a session-scoped background task, not detached nohup/setsid daemon A detached server would block live pushes from a later MCP session (see gotcha)

Pending Work

Immediate Next Steps

  1. Nothing blocking. Next real use is the IO-102 readings session (driven from ~/bin/claude_analog).
  2. Optional: fix reattach so live pushes work across processes (see gotcha). Simplest option: when workbench_scaffold finds a live server owned by another pid, have workbench_state POST the state to that server (e.g. a new POST /state endpoint) instead of broadcasting to an empty local client set.

Blockers/Open Questions

  • rival-llms MCP is also only in settings.json's mcpServers, so it doesn't load either. Not touched; ask Seth whether he wants it.

Deferred Items

  • The FastMCP serverInfo version reports 1.26.0 (the SDK's version), not 0.2.0. Cosmetic.
  • workbench serve <name> passes the name as the title, which overwrites index.html's title. Cosmetic.
  • No favicon (a 404 in the browser console). Cosmetic.

Context for Resuming Agent

Important Context

  • The README claim "reattach… browser stays connected" is only half true. The reattaching process has an empty ws_clients set, so workbench_state writes state.json but browsers see it only after a refresh.
  • POST /log has no auth. It's LAN-only, like the rest of the server. Anyone on the LAN can append log lines, but can't read files outside the project or inject script into the feed.

Assumptions Made

  • LAN trust model (no auth) is acceptable for Seth's homelab.

Potential Gotchas

  • Playwright's bundled browsers are not installed; use chromium.launch(executable_path="/usr/bin/chromium").
  • MCP tools only appear in Claude Code sessions started after registration.

Environment State

Tools/Services Used

  • workbench MCP (user scope), gitea push

Active Processes

  • None persistent. The io102 server from 2026-09-28 was session-scoped.

Environment Variables

  • None
  • ~/bin/claude_analog/.claude/handoffs/2026-09-28-163442-io102-focus-ready-for-readings.md
  • docs/superpowers/plans/2026-03-30-workbench-v2.md

Security Reminder: Before finalizing, run validate_handoff.py to check for accidental secret exposure.