8000 feat(assistant): turn the MCP runner and a browser on from the web app, with ready-made MCP servers by adeelahmad · Pull Request #491 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(assistant): turn the MCP runner and a browser on from the web app, with ready-made MCP servers - #491

Merged
adeelahmad merged 20 commits into
mainfrom
claude/project-thread-r960xc-c6-browser
Oct 18, 2026
Merged

adeelahmad merged 20 commits into
mainfrom
claude/project-thread-r960xc-c6-browser

Conversation

@adeelahmad
Copy link
Copy Markdown
Owner

Requested by Adeel · project thread

Description

Before: the MCP runner could only be turned on by editing .env and running the install line again, the assistant had no browser on the server, and adding a well-known MCP server meant typing it in by hand.

After: Add MCP server starts with Ready-made. Browser adds a headless Chromium the assistant drives (Playwright MCP) with one click once its container answers, and its switch turns the container on first. GitHub fills in GitHub's own server with a field for the token. The runner gets the same switch under Command. Values written as ${NAME} in a pasted config get a field of their own.

How: the host upgrade agent's action=services now takes profile= (a fixed list: mcp, browser, monitoring) and turn=on|off; the older monitoring=on|off request still works. domain/services.py writes those requests and follows them, one at a time, and monitoring goes through it too. The new browser Compose profile runs mcr.microsoft.com/playwright/mcp:v0.0.83 hardened (caps dropped, read-only, memory and process limits, profile in memory) on an internal network whose only way out is browser-proxy: netguard.py run on its own, which reaches public addresses on ports 80 and 443 only, with a byte budget per connection. A spec with keep: true makes Lens reuse one MCP session between calls (the browser keeps its open page), and it opens a new one when the server forgets it. The preset switches browser_run_code_unsafe off and lets navigating run at once until outside text was read.

This also makes mail-routes.test.tsx wait long enough for its 600 ms debounced preview, which failed under load in local CI.

Motivation and Context

Part of the chat-app gaps: settings for chat tools and MCP servers, and a browser MCP. A follow-up adds the MCP servers block in Settings › AI assistant.

Local CI (ci/local-ci.sh): backend pass on 96e8835 (1373 passed); Next.js, pre-commit and PR title pass on 969c44d (the head, which only adds the test fix).

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change; ! in the title)

Checklist:

  • The title is a Conventional Commit.
  • My code follows the code style of this project (make hooks checks it on commit).
  • I added or updated tests: test_services.py, test_netguard_forwarder.py, the browser preset and kept sessions in test_mcp_servers.py, test_mcp.py, test_monitoring.py, mcp-server-ui.test.tsx, packaging/host/test-services.sh (a new host-agent Nx project), and .github/workflows/browser.yml, which starts the two containers and checks the sandbox (private and metadata addresses refused, no direct way out, wrong Host refused, a page kept between sessions).
  • My change requires documentation updates, and I have updated docs/ accordingly: assistant.md (The browser, Optional containers, Ready-made, keep), mcp.md, updates.md, prometheus.md, contributing.md.
  • Users need more than the title to understand it, so I wrote it under ## Unreleased in CHANGELOG.md.

Done questions (AGENTS.md)

  • Goal: delivers what the user wanted, merged on green CI. Not merged yet; the Settings block follows in the next PR.
  • Agents: GET /api/v1/admin/services, PUT /api/v1/admin/services/{profile}, GET /api/v1/extensions/mcp/presets, and the MCP tools services and set_service.
  • Zero setup: the switch has the upgrade agent edit COMPOSE_PROFILES and start the containers; without an agent it shows the one line to run.
  • Fewest clicks and keystrokes: one switch, then Add Browser; the token field has focus.
  • Configurable: everything is in the web app; nothing new in the setup wizard.
  • Light: the browser (about 400 MB to download, at most 1 GB of memory) is off until turned on.
  • Private and secure: the browser has no Lens data or keys, its only way out is the guarded proxy, and the threat model covers the browser, the services requests, kept sessions and signing in to the shared browser (companions updated, ci/validate_threat_model.py passes).
  • Cost: no model calls added; MCP calls stay in the activity ledger (mcp.call).
  • User decides: everything is off until an admin turns it on; clicking, typing and filling forms in the browser ask first; existing servers keep a new session per call.
  • Upgrades: n/a for an upgrade step. The monitoring row keeps its id and shape; said is a new optional field read with a default.
  • Honest: docs say what is built; non-Docker installs don't have the browser yet, and docs say so.
  • Tests, docs and generated files updated (make openapi).
  • Open standards; dependencies keep Lens MIT: Playwright MCP is Apache-2.0 and runs as an optional container.
  • Result reported with evidence; "refine later" items written down: the browser for non-Docker installs; screenshots for vision models (--image-responses=omit today); a browser per person instead of one shared.

Generated by Claude Code

…p, with ready-made MCP servers

The host upgrade agent's services action now takes a profile (mcp,
browser or monitoring, a fixed list) and turn=on|off, so the web app,
the API (GET /admin/services, PUT /admin/services/{profile}) and the MCP
tools services and set_service can start or stop any of Lens's optional
containers; the old monitoring=on|off request still works.

The new browser profile runs Playwright MCP in a hardened container on
an internal network whose only way out is browser-proxy, netguard run
on its own (public addresses, ports 80 and 443, a byte budget per
connection). Add MCP server gains Ready-made: Browser (one click once
its container answers, browser_run_code_unsafe off) and GitHub (a field
for the token). ${NAME} values get their own fields, and a spec with
keep: true reuses one MCP session between calls.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
…-r960xc-c6-browser

# Conflicts:
#	threat-model.json
#	threat-model.md
#	threat-model.yaml
…machine

The preview waits 600 ms after the last keystroke, and findByText gives
up after 1 s, so the test failed when the machine was busy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
@adeelahmad adeelahmad self-assigned this Oct 17, 2026
claude added 16 commits October 17, 2026 04:35
The tmpfs at /home/node was root's, so Playwright couldn't make its profile
there and every navigation failed with EACCES. Mount it with node's uid and
gid (1000), in both compose files.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
Ported from #512 so this PR's upgrade check passes while that lands: the
entity-types step re-types weekday Terms as Dates, which the default entity
list leaves out, so the check read them as gone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
# Conflicts:
#	docker-compose.prod.yml
#	docker-compose.yml
#	docs/mcp.md
#	fastapi_backend/app/api/mcp_tools.py
#	fastapi_backend/app/api/v1/routes/updates.py
#	fastapi_backend/app/schemas/updates.py
#	fastapi_backend/tests/api/test_mcp.py
#	nextjs-frontend/app/openapi-client/index.ts
#	nextjs-frontend/app/openapi-client/sdk.gen.ts
#	nextjs-frontend/app/openapi-client/types.gen.ts
#	nextjs-frontend/openapi.json
#	threat-model.md
#	threat-model.yaml
# Conflicts:
#	docs/mcp.md
#	threat-model.yaml
# Conflicts:
#	threat-model.md
#	threat-model.yaml
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXPWXcSCQb9hWPr7xkZu7B
@adeelahmad
adeelahmad merged commit ec50a19 into main Oct 18, 2026
21 checks passed
@adeelahmad
adeelahmad deleted the claude/project-thread-r960xc-c6-browser branch October 18, 2026 02:30
adeelahmad pushed a commit that referenced this pull request Oct 18, 2026
…ay and clipper sign-in as built

Since the last README refresh, #474, #484, #510, #511, #497, #495, #406, #516, #517
and #491 merged. The README's built list now says a conversation exports as Markdown,
PDF or JSON and its answers, questions and code copy; a Tools chip picks an answer's
tools, limit and namespaces; sandbox runs open under the answer with downloads, and
Settings starts the gateway; deep research plans, runs and returns a cited report;
ready-made MCP servers (the browser, GitHub) and the optional containers start from
the web app; and the web clipper signs in or takes an API token. Each has its checks
in docs/requirements.md. In progress drops the merged PRs and lists the open feature
PRs; the Planned proof item keeps only the Raspberry Pi timing (#481 and #482 are
closed); the docs index links requirements and checks, and published feeds.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDx7PPUauyjMn1LZs8JAsS
adeelahmad pushed a commit that referenced this pull request Oct 18, 2026
…ay and clipper sign-in as built

Since the last README refresh, #474, #484, #510, #511, #497, #495, #406, #516, #517,
#491, #400 and #506 merged. The README's built list now says the assistant answers
questions about Lens itself; a conversation exports as Markdown, PDF or JSON and its
answers, questions and code copy; a Tools chip picks an answer's tools, limit and
namespaces; sandbox runs open under the answer with downloads, and Settings starts the
gateway; deep research plans, runs and returns a cited report; ready-made MCP servers,
the MCP servers page and the optional containers start from the web app; and the web
clipper signs in or takes an API token. Each has its checks in docs/requirements.md.
In progress lists only open PRs; the Nebula mesh (#546, merged, partly built) moves to
Planned with what's next; the Planned proof item keeps only the Raspberry Pi timing;
the docs index links requirements and checks, and published feeds.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDx7PPUauyjMn1LZs8JAsS
adeelahmad added a commit that referenced this pull request Oct 18, 2026
…ay and clipper sign-in as built (#536)

Since the last README refresh, #474, #484, #510, #511, #497, #495, #406, #516, #517,
#491, #400 and #506 merged. The README's built list now says the assistant answers
questions about Lens itself; a conversation exports as Markdown, PDF or JSON and its
answers, questions and code copy; a Tools chip picks an answer's tools, limit and
namespaces; sandbox runs open under the answer with downloads, and Settings starts the
gateway; deep research plans, runs and returns a cited report; ready-made MCP servers,
the MCP servers page and the optional containers start from the web app; and the web
clipper signs in or takes an API token. Each has its checks in docs/requirements.md.
In progress lists only open PRs; the Nebula mesh (#546, merged, partly built) moves to
Planned with what's next; the Planned proof item keeps only the Raspberry Pi timing;
the docs index links requirements and checks, and published feeds.


Claude-Session: https://claude.ai/code/session_01HDx7PPUauyjMn1LZs8JAsS

Co-authored-by: Adeel Ahmad <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0