docs: document manual setup in README - #20
Merged
Conversation
Adds the missing setup path from clone to working server: install commands, MCP-client registration, the two one-time credentials (browser session via an operator-launched Chrome with CDP attach — the only route Google's sign-in accepts — and the optional runtime-API OAuth consent), unattended operation via --auto-approve with an exported session file, and maintenance commands. Also documents the remote-Linux findings from the 2026-07-20 server run: xrdp/ software-rendered displays need Chrome launched with --disable-gpu, SSH shells need DISPLAY/XAUTHORITY/DBUS_SESSION_BUS_ADDRESS from the active desktop session before the keyring or a visible browser is reachable, and a crashed desktop session can drop the keyring token. Roadmap: §1 documentation bullet checked; stale wave-status snapshot (still describing PR #15 as open) refreshed to the post-#19 state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
henrik253
added a commit
that referenced
this pull request
Jul 21, 2026
* docs: require roadmap updates in completing commits * feat: wire managed-browser auto-approval into the MCP server CLI (#18) * docs: require roadmap updates in completing commits * feat: auto-approve Colab MCP connect dialog via managed Chromium Establishes the Phase 2 browser layer (plan.md §10/§11). The popup cannot be bypassed: Colab's connect-local-mcp command always awaits the dialog, it has no remember option, and the only dialog-free entry point is a private TEST_ONLY.connect() hook that would bypass a consent gate (plan.md §14). So we click it — only after proving the dialog is ours. Two gates exist, not one. Beyond Colab's dialog, Chrome blocks a public origin from reaching localhost (ERR_BLOCKED_BY_LOCAL_NETWORK). Modern Chrome uses the Local Network Access permission; the older Private Network Access response headers do not satisfy it. The controller grants local-network-access scoped to the Colab origin. Verification uses the dialog's own readonly token field, which Colab prefills with exactly <token>&<port>: compare against the session, refuse on mismatch, never leak the token. Success is the server-side connection, not the click. Mechanism verified by frontend-bundle analysis and a headless end-to-end spike against the real Colab UI plus a real ColabWebSocketServer; documented in plan.md §11 so a Colab UI change can be re-verified cheaply. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): unattended MCP approval via managed browser Wires the auto-approval into the demo so its phases stop needing three manual popup clicks each (demo README 'one MCP approval per tab'). - SessionManager takes an optional BrowserController; open_connection uses it to open and approve the tab, else falls back to webbrowser.open_new. The manager already owns one session per notebook_id and the controller owns one tab per notebook_id, so no new global or signature churn is needed. - run_demo.py gains --auto-approve/--headless and a 'login' command that opens the managed browser for the operator's one-time Google sign-in. Credentials are only ever typed by the operator and live in the gitignored profile. - Approval failures surface as structured errors, not hangs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): attach to the operator's own Chrome for auto-approval Google refuses sign-in to automated browsers: even launching the real Chrome binary, navigator.webdriver stays true and sign-in is blocked, so a Playwright-launched profile can never hold a usable session. Spoofing that signal would defeat a security check (plan.md §14), so instead the operator signs in to a normally-launched Chrome and the demo attaches to it. - BrowserController gains cdp_url (attach over CDP; never closes a browser it does not own) and use_chrome (real Chrome channel, automation flag dropped). - run_demo.py gains a 'chrome' command that launches Chrome with a debug port and its own profile (Chrome refuses remote debugging on the default profile), plus --cdp-url for the live phases. - Sign-in detection now waits for Colab's app shell before judging: a blank page trivially lacks the sign-in prompt and used to report success, closing the window before the operator could sign in. Regression covered by login_test.py. Verified live: three notebooks opened and auto-approved with no manual clicks; each runtime returned {connected: true, accelerator: CPU} from executed code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): headless operation via exported browser session A terminal-only server can now run signed in without a display, Chrome, Xvfb or a desktop. Google only checks for automation at sign-in, so a headless browser replaying an exported session is already authenticated — verified live: three notebooks connected headless from the session file alone, each runtime returning {connected: true, accelerator: CPU} from executed code. - BrowserController gains session_file (launch headless + storage_state) and export_session(), which writes the state 0600 before any bytes reach disk. A missing session returns user_action_required naming the fix. - run_demo.py gains export-session, session-check (exit 0/1, so a scheduler alerts instead of hanging) and --session-file. - CDP_URL now uses 127.0.0.1: Chrome binds the debug port on IPv4 while localhost may resolve to ::1 first and refuse the connection. The session file authenticates as the user with no password or 2FA, so it is treated as a credential throughout: 0600, gitignored, never logged, and documented as such. It is not interchangeable with the OAuth client JSON — that identifies the application, this identifies the user. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: wire managed-browser auto-approval into the MCP server CLI The auto-approval flow existed in the library and demo (BrowserController + SessionManager(browser=...) + server.open_connection) but the shipped entry point always built SessionManager() bare, so MCP clients still got the manual popup. New CLI flags close the gap: - --auto-approve: build + start a BrowserController, pass it to SessionManager, close it on shutdown (the manager never owns its lifecycle; a nested finally closes it even when a session refuses to close) - --cdp-url / --headless / --session-file configure the controller; they are rejected without --auto-approve, and --cdp-url rejects --headless / --session-file (an attached browser is whatever the operator launched) - behaviour is unchanged when the flags are absent; a start() failure aborts before serving, releases what start acquired, and user_action_required failures print their remedy instead of a traceback Roadmap §12 wiring bullet checked off with the cli_test.py::TestAutoApprove cases (happy paths, invalid flag combinations, failure paths). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * docs: document manual setup in README (#20) Adds the missing setup path from clone to working server: install commands, MCP-client registration, the two one-time credentials (browser session via an operator-launched Chrome with CDP attach — the only route Google's sign-in accepts — and the optional runtime-API OAuth consent), unattended operation via --auto-approve with an exported session file, and maintenance commands. Also documents the remote-Linux findings from the 2026-07-20 server run: xrdp/ software-rendered displays need Chrome launched with --disable-gpu, SSH shells need DISPLAY/XAUTHORITY/DBUS_SESSION_BUS_ADDRESS from the active desktop session before the keyring or a visible browser is reachable, and a crashed desktop session can drop the keyring token. Roadmap: §1 documentation bullet checked; stale wave-status snapshot (still describing PR #15 as open) refreshed to the post-#19 state. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
henrik253
added a commit
that referenced
this pull request
Jul 21, 2026
* docs: require roadmap updates in completing commits * feat: wire managed-browser auto-approval into the MCP server CLI (#18) * docs: require roadmap updates in completing commits * feat: auto-approve Colab MCP connect dialog via managed Chromium Establishes the Phase 2 browser layer (plan.md §10/§11). The popup cannot be bypassed: Colab's connect-local-mcp command always awaits the dialog, it has no remember option, and the only dialog-free entry point is a private TEST_ONLY.connect() hook that would bypass a consent gate (plan.md §14). So we click it — only after proving the dialog is ours. Two gates exist, not one. Beyond Colab's dialog, Chrome blocks a public origin from reaching localhost (ERR_BLOCKED_BY_LOCAL_NETWORK). Modern Chrome uses the Local Network Access permission; the older Private Network Access response headers do not satisfy it. The controller grants local-network-access scoped to the Colab origin. Verification uses the dialog's own readonly token field, which Colab prefills with exactly <token>&<port>: compare against the session, refuse on mismatch, never leak the token. Success is the server-side connection, not the click. Mechanism verified by frontend-bundle analysis and a headless end-to-end spike against the real Colab UI plus a real ColabWebSocketServer; documented in plan.md §11 so a Colab UI change can be re-verified cheaply. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): unattended MCP approval via managed browser Wires the auto-approval into the demo so its phases stop needing three manual popup clicks each (demo README 'one MCP approval per tab'). - SessionManager takes an optional BrowserController; open_connection uses it to open and approve the tab, else falls back to webbrowser.open_new. The manager already owns one session per notebook_id and the controller owns one tab per notebook_id, so no new global or signature churn is needed. - run_demo.py gains --auto-approve/--headless and a 'login' command that opens the managed browser for the operator's one-time Google sign-in. Credentials are only ever typed by the operator and live in the gitignored profile. - Approval failures surface as structured errors, not hangs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): attach to the operator's own Chrome for auto-approval Google refuses sign-in to automated browsers: even launching the real Chrome binary, navigator.webdriver stays true and sign-in is blocked, so a Playwright-launched profile can never hold a usable session. Spoofing that signal would defeat a security check (plan.md §14), so instead the operator signs in to a normally-launched Chrome and the demo attaches to it. - BrowserController gains cdp_url (attach over CDP; never closes a browser it does not own) and use_chrome (real Chrome channel, automation flag dropped). - run_demo.py gains a 'chrome' command that launches Chrome with a debug port and its own profile (Chrome refuses remote debugging on the default profile), plus --cdp-url for the live phases. - Sign-in detection now waits for Colab's app shell before judging: a blank page trivially lacks the sign-in prompt and used to report success, closing the window before the operator could sign in. Regression covered by login_test.py. Verified live: three notebooks opened and auto-approved with no manual clicks; each runtime returned {connected: true, accelerator: CPU} from executed code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): headless operation via exported browser session A terminal-only server can now run signed in without a display, Chrome, Xvfb or a desktop. Google only checks for automation at sign-in, so a headless browser replaying an exported session is already authenticated — verified live: three notebooks connected headless from the session file alone, each runtime returning {connected: true, accelerator: CPU} from executed code. - BrowserController gains session_file (launch headless + storage_state) and export_session(), which writes the state 0600 before any bytes reach disk. A missing session returns user_action_required naming the fix. - run_demo.py gains export-session, session-check (exit 0/1, so a scheduler alerts instead of hanging) and --session-file. - CDP_URL now uses 127.0.0.1: Chrome binds the debug port on IPv4 while localhost may resolve to ::1 first and refuse the connection. The session file authenticates as the user with no password or 2FA, so it is treated as a credential throughout: 0600, gitignored, never logged, and documented as such. It is not interchangeable with the OAuth client JSON — that identifies the application, this identifies the user. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: wire managed-browser auto-approval into the MCP server CLI The auto-approval flow existed in the library and demo (BrowserController + SessionManager(browser=...) + server.open_connection) but the shipped entry point always built SessionManager() bare, so MCP clients still got the manual popup. New CLI flags close the gap: - --auto-approve: build + start a BrowserController, pass it to SessionManager, close it on shutdown (the manager never owns its lifecycle; a nested finally closes it even when a session refuses to close) - --cdp-url / --headless / --session-file configure the controller; they are rejected without --auto-approve, and --cdp-url rejects --headless / --session-file (an attached browser is whatever the operator launched) - behaviour is unchanged when the flags are absent; a start() failure aborts before serving, releases what start acquired, and user_action_required failures print their remedy instead of a traceback Roadmap §12 wiring bullet checked off with the cli_test.py::TestAutoApprove cases (happy paths, invalid flag combinations, failure paths). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * docs: document manual setup in README (#20) Adds the missing setup path from clone to working server: install commands, MCP-client registration, the two one-time credentials (browser session via an operator-launched Chrome with CDP attach — the only route Google's sign-in accepts — and the optional runtime-API OAuth consent), unattended operation via --auto-approve with an exported session file, and maintenance commands. Also documents the remote-Linux findings from the 2026-07-20 server run: xrdp/ software-rendered displays need Chrome launched with --disable-gpu, SSH shells need DISPLAY/XAUTHORITY/DBUS_SESSION_BUS_ADDRESS from the active desktop session before the keyring or a visible browser is reachable, and a crashed desktop session can drop the keyring token. Roadmap: §1 documentation bullet checked; stale wave-status snapshot (still describing PR #15 as open) refreshed to the post-#19 state. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * docs: agent-facing feature guide (#23) Agents connected to the server were observed using only a fraction of the tool surface. Two additions fix discovery: - docs/agent-guide.md: one-page agent guide — mental model (registered notebooks are persistent resources, sessions are replaceable), a goal → tool map of all 31 tools, standard workflows (project setup, work session, GPU switch with safe endpoint discovery, recovery), env vars, and pitfalls (close does not sync, local-path reopen resets to last-synced file, GitHub URLs load from the remote branch, endpoints are not mapped to notebooks, structured errors). - README "For agents" section: the whole surface in ten lines, linking to the guide, placed before Architecture so an agent reading the README top-down knows every feature before any implementation detail. All tool names, signatures, profile names, and env vars verified against registry/tools.py, snapshots/tools.py, transfer_tools.py, runtime/tools.py, server.py, and constants.py; plan-reviewer findings (endpoint discovery flow, default-session semantics, local-path reopen data loss, snapshot outputs not re-injected) are incorporated. Out-of-plan docs scope, user-requested; recorded in the roadmap wave status. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * docs: mark agent guide merged in roadmap wave status Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
henrik253
added a commit
that referenced
this pull request
Jul 21, 2026
* docs: require roadmap updates in completing commits * feat: wire managed-browser auto-approval into the MCP server CLI (#18) * docs: require roadmap updates in completing commits * feat: auto-approve Colab MCP connect dialog via managed Chromium Establishes the Phase 2 browser layer (plan.md §10/§11). The popup cannot be bypassed: Colab's connect-local-mcp command always awaits the dialog, it has no remember option, and the only dialog-free entry point is a private TEST_ONLY.connect() hook that would bypass a consent gate (plan.md §14). So we click it — only after proving the dialog is ours. Two gates exist, not one. Beyond Colab's dialog, Chrome blocks a public origin from reaching localhost (ERR_BLOCKED_BY_LOCAL_NETWORK). Modern Chrome uses the Local Network Access permission; the older Private Network Access response headers do not satisfy it. The controller grants local-network-access scoped to the Colab origin. Verification uses the dialog's own readonly token field, which Colab prefills with exactly <token>&<port>: compare against the session, refuse on mismatch, never leak the token. Success is the server-side connection, not the click. Mechanism verified by frontend-bundle analysis and a headless end-to-end spike against the real Colab UI plus a real ColabWebSocketServer; documented in plan.md §11 so a Colab UI change can be re-verified cheaply. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): unattended MCP approval via managed browser Wires the auto-approval into the demo so its phases stop needing three manual popup clicks each (demo README 'one MCP approval per tab'). - SessionManager takes an optional BrowserController; open_connection uses it to open and approve the tab, else falls back to webbrowser.open_new. The manager already owns one session per notebook_id and the controller owns one tab per notebook_id, so no new global or signature churn is needed. - run_demo.py gains --auto-approve/--headless and a 'login' command that opens the managed browser for the operator's one-time Google sign-in. Credentials are only ever typed by the operator and live in the gitignored profile. - Approval failures surface as structured errors, not hangs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): attach to the operator's own Chrome for auto-approval Google refuses sign-in to automated browsers: even launching the real Chrome binary, navigator.webdriver stays true and sign-in is blocked, so a Playwright-launched profile can never hold a usable session. Spoofing that signal would defeat a security check (plan.md §14), so instead the operator signs in to a normally-launched Chrome and the demo attaches to it. - BrowserController gains cdp_url (attach over CDP; never closes a browser it does not own) and use_chrome (real Chrome channel, automation flag dropped). - run_demo.py gains a 'chrome' command that launches Chrome with a debug port and its own profile (Chrome refuses remote debugging on the default profile), plus --cdp-url for the live phases. - Sign-in detection now waits for Colab's app shell before judging: a blank page trivially lacks the sign-in prompt and used to report success, closing the window before the operator could sign in. Regression covered by login_test.py. Verified live: three notebooks opened and auto-approved with no manual clicks; each runtime returned {connected: true, accelerator: CPU} from executed code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(demo): headless operation via exported browser session A terminal-only server can now run signed in without a display, Chrome, Xvfb or a desktop. Google only checks for automation at sign-in, so a headless browser replaying an exported session is already authenticated — verified live: three notebooks connected headless from the session file alone, each runtime returning {connected: true, accelerator: CPU} from executed code. - BrowserController gains session_file (launch headless + storage_state) and export_session(), which writes the state 0600 before any bytes reach disk. A missing session returns user_action_required naming the fix. - run_demo.py gains export-session, session-check (exit 0/1, so a scheduler alerts instead of hanging) and --session-file. - CDP_URL now uses 127.0.0.1: Chrome binds the debug port on IPv4 while localhost may resolve to ::1 first and refuse the connection. The session file authenticates as the user with no password or 2FA, so it is treated as a credential throughout: 0600, gitignored, never logged, and documented as such. It is not interchangeable with the OAuth client JSON — that identifies the application, this identifies the user. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: wire managed-browser auto-approval into the MCP server CLI The auto-approval flow existed in the library and demo (BrowserController + SessionManager(browser=...) + server.open_connection) but the shipped entry point always built SessionManager() bare, so MCP clients still got the manual popup. New CLI flags close the gap: - --auto-approve: build + start a BrowserController, pass it to SessionManager, close it on shutdown (the manager never owns its lifecycle; a nested finally closes it even when a session refuses to close) - --cdp-url / --headless / --session-file configure the controller; they are rejected without --auto-approve, and --cdp-url rejects --headless / --session-file (an attached browser is whatever the operator launched) - behaviour is unchanged when the flags are absent; a start() failure aborts before serving, releases what start acquired, and user_action_required failures print their remedy instead of a traceback Roadmap §12 wiring bullet checked off with the cli_test.py::TestAutoApprove cases (happy paths, invalid flag combinations, failure paths). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * docs: document manual setup in README (#20) Adds the missing setup path from clone to working server: install commands, MCP-client registration, the two one-time credentials (browser session via an operator-launched Chrome with CDP attach — the only route Google's sign-in accepts — and the optional runtime-API OAuth consent), unattended operation via --auto-approve with an exported session file, and maintenance commands. Also documents the remote-Linux findings from the 2026-07-20 server run: xrdp/ software-rendered displays need Chrome launched with --disable-gpu, SSH shells need DISPLAY/XAUTHORITY/DBUS_SESSION_BUS_ADDRESS from the active desktop session before the keyring or a visible browser is reachable, and a crashed desktop session can drop the keyring token. Roadmap: §1 documentation bullet checked; stale wave-status snapshot (still describing PR #15 as open) refreshed to the post-#19 state. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * docs: agent-facing feature guide (#23) Agents connected to the server were observed using only a fraction of the tool surface. Two additions fix discovery: - docs/agent-guide.md: one-page agent guide — mental model (registered notebooks are persistent resources, sessions are replaceable), a goal → tool map of all 31 tools, standard workflows (project setup, work session, GPU switch with safe endpoint discovery, recovery), env vars, and pitfalls (close does not sync, local-path reopen resets to last-synced file, GitHub URLs load from the remote branch, endpoints are not mapped to notebooks, structured errors). - README "For agents" section: the whole surface in ten lines, linking to the guide, placed before Architecture so an agent reading the README top-down knows every feature before any implementation detail. All tool names, signatures, profile names, and env vars verified against registry/tools.py, snapshots/tools.py, transfer_tools.py, runtime/tools.py, server.py, and constants.py; plan-reviewer findings (endpoint discovery flow, default-session semantics, local-path reopen data loss, snapshot outputs not re-injected) are incorporated. Out-of-plan docs scope, user-requested; recorded in the roadmap wave status. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * docs: mark agent guide merged in roadmap wave status Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(demo): satisfy E402 in drive-mount cells PR #26 merged with `import os` placed after the drive.mount call in the two drive-mount demo cells, which fails ruff E402 and turned CI red on main. Move the import to the top of the cell. Roadmap: record PR #26 (merged directly to main without a roadmap update) in the wave status. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * style: ruff-format drive-mount notebooks Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands roadmap §1's "Document the manual setup steps in README.md" bullet.
operator-launched Chrome with CDP attach (the only sign-in route Google accepts), and
the optional runtime-API OAuth consent
--auto-approve, and headless servers via an exported sessionfile, with the session file documented as a credential
--disable-gpuonsoftware-rendered displays, SSH session-env wiring, keyring loss on session crash
doctor,--list-running,--kill-staleto the post-feat: unattended MCP approval via server CLI flags #19 state. The §1 end-to-end bullet stays open (T4 still unverified).
Plan-reviewer verdict: APPROVE — all documented flags/paths verified against the code on
this branch; no uncommitted server-side behavior is documented. Its one wording finding
is fixed in this commit. Two pre-existing issues it found outside the diff are tracked
separately (run_demo.py live phases drop --cdp-url/--session-file; duplicate
BROWSER_PROFILE_DIR_NAME constant).
Docs-only: 397 tests pass, ruff clean.
🤖 Generated with Claude Code