Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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)
a6766e8docs: document page write-back via POST /log05fcb7ffeat: POST /log from the page + load state.json on page open6562250fix: block path traversal in static handler4b55135docs: add open source subheadingc97adfcdocs: 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 aretemplate(HTML),styles,script(run vianew Function). On load: fetchstate.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_workbenchfixture; live-server tests callserver._start_http_serverdirectly 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.jsonbacked 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
- Nothing blocking. Next real use is the IO-102 readings session (driven from
~/bin/claude_analog). - Optional: fix reattach so live pushes work across processes (see gotcha). Simplest option: when
workbench_scaffoldfinds a live server owned by another pid, haveworkbench_statePOST the state to that server (e.g. a newPOST /stateendpoint) instead of broadcasting to an empty local client set.
Blockers/Open Questions
rival-llmsMCP is also only insettings.json'smcpServers, so it doesn't load either. Not touched; ask Seth whether he wants it.
Deferred Items
- The FastMCP
serverInfoversion 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_statewrites state.json but browsers see it only after a refresh. POST /loghas 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
workbenchMCP (user scope),gitea push
Active Processes
- None persistent. The io102 server from 2026-09-28 was session-scoped.
Environment Variables
- None
Related Resources
~/bin/claude_analog/.claude/handoffs/2026-09-28-163442-io102-focus-ready-for-readings.mddocs/superpowers/plans/2026-03-30-workbench-v2.md
Security Reminder: Before finalizing, run validate_handoff.py to check for accidental secret exposure.