A Python automation browser that ships its own Python inside the app so the user never touches pip, venv, or Terminal.
Every top SERP result for this keyword (Selenium with Python, Playwright Python, Helium, Pyppeteer, browser-use, ZenRows roundups) is a developer library that assumes you have Python on PATH. Fazm is a consumer Mac app that ships Python 3.12 inside Fazm.app/Contents/Resources, tells its agent at startup that bare python3 is not available, and drives your real Chrome through the Playwright MCP Bridge extension. The system prompt literally contains the path /Applications/Fazm.app/Contents/Resources/google-workspace-mcp/.venv/bin/python3.
“The anchor fact no competitor page mentions. Fazm's agent is given this exact line in its system prompt at session start, interpolated from Desktop/Sources/Chat/ChatPrompts.swift line 21: 'Python: {bundled_python_path} (bundled — never use bare python3/pip3, user may not have Python installed)'. The token {bundled_python_path} resolves, via the bundledPythonPath property on line 745 of the same file, to Bundle.main.bundlePath + '/Contents/Resources/google-workspace-mcp/.venv/bin/python3'. On a user's Mac that is /Applications/Fazm.app/Contents/Resources/google-workspace-mcp/.venv/bin/python3. That venv was built with 'uv venv --python python3.12 --relocatable' (build.sh line 210), libpython3.12.dylib was copied into .venv/lib at build.sh line 237, and pyvenv.cfg was rewritten to 'home = bin' at line 244 so stdlib resolves relative to the venv on any machine the app lands on.”
/Users/matthewdi/fazm/Desktop/Sources/Chat/ChatPrompts.swift:21 + /Users/matthewdi/fazm/build.sh:199-252
How a sentence becomes a Python call and a browser click
A single user sentence can trigger the bundled Python interpreter, the Playwright MCP, and the native macOS Accessibility APIs inside one conversation turn. The agent's system prompt knows where each one lives.
From one English sentence to Python + Chrome in one turn
The four build-time moves that make Python portable inside the app
Relocatable venv, PYTHONHOME wrapper, dylib copy, pyvenv.cfg rewrite. Drop any of the four and the interpreter breaks on any machine that is not the build machine.
Python 3.12 binary, relocatable
build.sh line 210 runs 'uv venv --python python3.12 --relocatable'. Relocatable means the venv does not bake absolute paths into its scripts, so dragging Fazm.app between /Applications, /Users/<you>/Applications, or ~/Desktop does not break the interpreter.
Shell wrapper that sets PYTHONHOME
The python3 and python3.12 entries in .venv/bin/ are shell wrappers (build.sh lines 227 to 233) that compute VENV_DIR from their own path, export PYTHONHOME=$VENV_DIR, then exec the real CPython binary. No matter where the app lives, the interpreter finds its stdlib.
libpython3.12.dylib copied in
build.sh line 237 copies libpython3.12.dylib into .venv/lib/ so @executable_path/../lib/libpython3.12.dylib resolves at runtime. Without this copy, the bundled binary links against a dylib that only exists on the build machine.
pyvenv.cfg rewritten to 'home = bin'
build.sh line 244 overwrites pyvenv.cfg so Python finds stdlib relative to the venv, not the uv-managed install on the build machine. Without this rewrite, any user who installs Fazm would see 'ModuleNotFoundError: No module named encodings' on first launch.
The line of the system prompt that changes everything
Most “AI agent” products quietly assume the user can install things. Fazm's agent is told the opposite, as a fact, before it writes a single token.
How Python 3.12 actually gets inside the .app bundle
The build script is readable. No magic. Four things have to happen in this exact order for the interpreter to survive being copied to a user's /Applications folder.
How Fazm launches Python at runtime without breaking its code signature
PYTHONDONTWRITEBYTECODE=1 is the non-obvious line. Without it, Python writes .pyc files into the app bundle on first import, the code signature goes invalid, and Sparkle refuses to auto-update. Fazm sets it before spawning the Python MCP server.
What the user sees on a clean Mac with no Python installed
Simulated first-run trace on a freshly-imaged Mac. The user opens Terminal only to prove Python is not installed; Fazm does not need it.
Python library vs Python-bundled app
Same keyword, two different products. A developer-facing library you pip install on top of your own Python, or a consumer Mac app that ships Python inside itself and drives your real Chrome.
| Feature | Typical Python library (Selenium, Playwright, Helium, browser-use) | Fazm (Python bundled inside the app) |
|---|---|---|
| Does the reader need to install Python? | Yes. Selenium, Playwright Python, Helium, Pyppeteer, browser-use all assume you have a working Python toolchain on PATH and that 'pip install <package>' will succeed. On a fresh macOS the user hits xcode-select, certifi, and venv permission issues before their first line of automation runs. | No. Fazm ships Python 3.12 inside its .app bundle at Contents/Resources/google-workspace-mcp/.venv/bin/python3. The build script (build.sh line 210) creates it with 'uv venv --python python3.12 --relocatable' and then copies libpython3.12.dylib and the full stdlib in so the interpreter is portable. |
| What command the user types to automate | A Python script. 'from playwright.sync_api import sync_playwright; with sync_playwright() as p: ...' Every run starts with a venv decision and a selector decision. | A sentence in the Fazm floating bar. 'Open Stripe, download the last three invoices as PDFs.' The agent is told at startup that Python 3.12 is already bundled, so when it decides to reach for a Python library (telethon for Telegram, pandas for a CSV, etc.), it prefixes the path shown in its system prompt. |
| What browser the automation drives | A fresh Chromium Playwright downloaded to ~/.cache/ms-playwright. Blank profile, no cookies, no extensions, CDP webdriver flag on by default. | Your real running Chrome. Fazm connects over CDP through the Playwright MCP Bridge extension (Chrome Web Store ID mmlmfjhmonkocbjadbfplnigmagldckm). Your cookies, 2FA state, and Cloudflare trust cookie are live on turn one. |
| What the agent reads to decide what to click | Depends on the library. Selenium wants CSS or XPath selectors you wrote. Browser-use and similar screenshot-based agents ask a vision model to guess x,y pixels. Every redesign breaks the second. | An accessibility snapshot returned from browser_snapshot. Every input has a human-readable label and a [ref=eN] token. The agent sends text, not pixel coordinates. For non-browser apps it reads the AXUIElement tree via the bundled macos-use server, not a screenshot. |
| What happens to .pyc files during a run | Standard Python behavior: __pycache__ directories scattered wherever your script lives. | Never written. acp-bridge/src/index.ts line 1092 sets PYTHONDONTWRITEBYTECODE=1 on the Python server. Python writing a .pyc into the app bundle would invalidate the code signature and break Sparkle auto-updates, so the flag is a hard requirement. |
| Is this a library, a framework, or an app? | A library or framework. The user is expected to write a program, maintain it, and keep it working through browser and site updates. | A signed, notarized macOS .app downloaded from fazm.ai. The user double-clicks it. There is no package.json, no requirements.txt, no 'source .venv/bin/activate' in their life. |
| Where Python stdlib lives on the user's disk | Wherever the user's system Python was installed. Often broken after an OS upgrade, or missing because the user never ran 'brew install python'. | Inside the app bundle at Contents/Resources/google-workspace-mcp/.venv/lib/python3.12, rsynced from the uv-managed install at build time (build.sh line 241). pyvenv.cfg is rewritten to 'home = bin' so the interpreter finds stdlib relative to itself, no matter where the user drags the app. |
| How Python and the browser talk to each other | The Python script is the orchestrator. It imports Playwright, writes a script, runs it to completion, then the process exits. | Through MCP. The Python Google Workspace server is one process, the Node acp-bridge is another, the Playwright MCP with --extension is a third. The Swift Fazm UI routes tool calls between them, so a single user sentence can chain a Python call and a browser_click in the same turn. |
| Runs on a Mac with zero prior dev setup | No. A fresh Mac needs Xcode command-line tools, then Homebrew, then Python, then a venv, then pip install, then likely a certifi patch before HTTPS works. | Yes. Download the .dmg, drag to Applications, open it, paste the Chrome extension token, done. We have tested this path on freshly-imaged Macs. |
Everything Fazm wires together so the user does not have to
The build choices, the runtime env vars, the extension handshake, and the overlay paint, all invisible to the person who just said “automate my browser with Python.”
Six steps, from download to a running Python + Chrome automation
Steps one through four happen once, invisibly. Step five is every sentence the user ever types. Step six is the result.
You download Fazm.app
Signed, notarized .dmg from fazm.ai. Drag to /Applications. No Homebrew, no Xcode, no Python, no pip. The bundle weighs roughly 400 MB because the Python 3.12 interpreter and its stdlib ride along.
Fazm opens. Python is already wired.
On first launch, ChatProvider.swift builds the system prompt by interpolating {bundled_python_path} with the path to the bundled interpreter. Line 21 of ChatPrompts.swift reads: 'Python: {bundled_python_path} (bundled — never use bare python3/pip3, user may not have Python installed)'. The agent reads that sentence before its first token.
Fazm starts the Python MCP server
acp-bridge/src/index.ts line 1076 checks that googleWorkspaceMcpPython and googleWorkspaceMcpMain both exist, then spawns Python with main.py and --transport stdio. Env: PYTHONHOME points at the bundled venv, PYTHONDONTWRITEBYTECODE=1 blocks .pyc writes.
Fazm wires the Playwright MCP to your real Chrome
The Node bridge spawns @playwright/mcp with --extension. The extension (Chrome Web Store ID mmlmfjhmonkocbjadbfplnigmagldckm) attaches over CDP to the Chrome you already use. One token paste from the extension popup is the entire auth.
You type your first sentence
'Open Stripe, download the last three invoices as PDFs, rename them by date, and drop them in my Dropbox.' The agent reasons about which tool does which piece: Playwright for the browser, the bundled Python for anything that calls Google APIs, macos-use for the Finder rename.
The run happens, visibly
A glowing halo paints the automated Chrome tab. A pill reading 'Browser controlled by Fazm · Feel free to switch tabs or use other apps' appears at the top. You go read another tab. The agent finishes. Three PDFs in your Dropbox.
The boundary, point by point
Bundling Python inside an app is a bigger trust surface than shipping a thin browser wrapper. These are the specific design choices that keep it contained.
What the bundled Python does and does not touch on your Mac
- The bundled interpreter path is injected into the agent's system prompt via the {bundled_python_path} token on ChatPrompts.swift line 21; the value comes from Bundle.main.bundlePath in the same file, line 745.
- PYTHONDONTWRITEBYTECODE=1 is set on the Python MCP server's env at acp-bridge/src/index.ts line 1092, so no .pyc files are written inside the app bundle.
- pyvenv.cfg includes 'include-system-site-packages = false', so the bundled Python never reads from the user's /opt/homebrew or ~/.pyenv.
- The Python MCP server speaks stdio, not a network socket; nothing listens on a port.
- The bundled Python is spawned only if both the interpreter binary and main.py exist on disk (acp-bridge/src/index.ts line 1077), so a corrupted app bundle fails closed.
- Any user-added MCP server from ~/.fazm/mcp-servers.json runs with whatever command the user wrote, outside the sandbox of the bundled venv, so advanced users keep full control.
Python automation without the Python setup tax.
Fazm is a Mac app that ships Python 3.12 inside the bundle, drives your real Chrome through an accessibility-first automation pipeline, and takes natural-language commands. Free to download, one-time install.
Download Fazm →See Python + Chrome automation running inside the Fazm app
Book 20 minutes and we'll walk through the bundled Python path, the Playwright MCP attach, and one automation you pick live on the call.
Book a call →Python automation browser, answered against the Fazm source
What does 'python automation browser' mean in the context of Fazm if Fazm is a Mac app, not a Python library?
It means an end user can say a sentence in plain English like 'write a Python script that opens Stripe, pulls the last three invoices, and saves them as PDFs,' and Fazm will actually execute it, because Fazm ships its own Python 3.12 interpreter inside the .app bundle and its agent uses that interpreter for any Python work. The alternative, every other library on the first SERP page, is a developer tool that assumes you already have Python on PATH. The Fazm agent's system prompt is literally templated with the absolute path to the bundled interpreter at session start (Desktop/Sources/Chat/ChatPrompts.swift line 21: 'Python: {bundled_python_path} (bundled — never use bare python3/pip3, user may not have Python installed)'), and the path resolves to /Applications/Fazm.app/Contents/Resources/google-workspace-mcp/.venv/bin/python3.
Why bundle Python inside a Mac app instead of just telling the user to install Python?
Because the goal is a product that works on first run for a person who has never opened Terminal. Telling a non-developer Mac user to 'install Python' means, in practice, running xcode-select --install, installing Homebrew, installing Python, fixing SSL certificates for certifi, creating a venv, and running pip install. Each of those steps has a well-known failure mode on a clean macOS machine. By bundling a relocatable Python 3.12 built with uv, plus libpython3.12.dylib, plus the full standard library under .venv/lib/python3.12, plus a shell wrapper that sets PYTHONHOME, Fazm turns all of that into a double-click install. The build.sh script handles it at lines 199 to 252.
What actually runs in Python inside Fazm?
The Google Workspace MCP server. It is a Python project launched by acp-bridge/src/index.ts line 1076 with 'command: googleWorkspaceMcpPython' and 'args: [googleWorkspaceMcpMain, --transport, stdio]'. It exposes Gmail, Calendar, Drive, and Docs as MCP tools. Any future Python MCP server the user adds (from ~/.fazm/mcp-servers.json) is also expected to use the bundled interpreter's path, because the agent is told at startup that bare python3 is not available. The browser itself is driven by the TypeScript-based Playwright MCP, but the two servers coordinate inside one user turn, so 'Python' and 'browser' are both happening in a single conversation.
Is the bundled Python hermetic, or does it leak into the user's environment?
Hermetic by design. The wrapper scripts at .venv/bin/python3 and .venv/bin/python3.12 (build.sh lines 227 to 233) compute VENV_DIR from their own location and export PYTHONHOME before exec'ing the real CPython binary. That means stdlib resolution is always relative to the app bundle. pyvenv.cfg is rewritten to 'home = bin' (build.sh line 244) so site-packages is pinned to the bundle. And 'include-system-site-packages = false' in the same pyvenv.cfg means your ~/.pyenv or /opt/homebrew/bin/python is never consulted. Your Python installation, if any, is not touched.
Why is PYTHONDONTWRITEBYTECODE=1 set when the bundled Python runs?
Because macOS code-signs the app bundle, and Sparkle (the auto-update framework) verifies that signature before applying updates. If Python writes .pyc files into the bundle's site-packages on first import, the bundle's on-disk state diverges from the signed state and Sparkle refuses to update. The fix is one env var: acp-bridge/src/index.ts line 1092 sets PYTHONDONTWRITEBYTECODE=1 on the server's environment. Python loads .py files, never writes .pyc back, and the signature stays valid.
How is this different from just running a Selenium or Playwright Python script?
Two differences. First, the browser Fazm drives is your real running Chrome, attached over CDP through the Playwright MCP Bridge Chrome Web Store extension (ID mmlmfjhmonkocbjadbfplnigmagldckm), not a fresh Chromium downloaded into ~/.cache/ms-playwright. That means your existing cookies, 2FA state, WebAuthn credentials, and Cloudflare session are all live on turn one. Second, the agent reads an accessibility snapshot returned by browser_snapshot (labelled DOM nodes with [ref=eN] tokens), not CSS selectors you wrote or screenshots a vision model has to guess coordinates from. Neither difference has anything to do with Python as a language; they are consequences of Fazm being an AI-driven product rather than a script runner.
Does Fazm work with any Mac app, or only the browser?
Any Mac app, through the bundled macos-use MCP server (acp-bridge/src/index.ts line 1059). macos-use reads AXUIElement trees from the macOS Accessibility APIs, which is the same machinery VoiceOver uses. That means Fazm can drive Finder, Settings, Mail, Notes, Figma, Xcode, and any native or Catalyst Mac app, not just a browser tab. The browser is one MCP server among five; the other four are fazm_tools, macos-use, whatsapp, and google-workspace. In a typical session, the agent chains calls across all of them inside a single user turn.
What if the user's Python script needs a dependency that is not in the bundled venv?
Options in order of preference. One, the agent can run the user's command with a one-off 'uv run --with package' if the user has uv installed. Two, the user can add a local MCP server config at ~/.fazm/mcp-servers.json pointing at their own Python venv; the bridge loads that file at startup (acp-bridge/src/index.ts line 1104) and spawns their server alongside the bundled ones. Three, the specific case of 'I want to use telethon' is covered by the Telegram bundled skill (Desktop/Sources/BundledSkills/telegram.skill.md), which is taught to the agent as a preferred path over browser automation for that one service.
How big is Fazm.app because of the bundled Python?
Roughly 400 MB including every bundled MCP server, the Node runtime for acp-bridge, the Swift UI binary, Playwright's Chromium-less install (because Fazm uses your real Chrome, Playwright MCP does not ship its own browser), and the Python 3.12 interpreter plus stdlib plus google-workspace-mcp dependencies. The Python payload itself is on the order of 100 to 150 MB of that, dominated by the stdlib copy at .venv/lib/python3.12 (rsynced in at build.sh line 241) and the libpython3.12.dylib copied in at line 237.
What if the user already has Python installed, will Fazm use that instead?
No, on purpose. The system prompt instructs the agent to never use bare python3 or pip3 regardless of what the user has installed, because the agent cannot reliably know whether the user's Python is a working version. The bundled interpreter has the known-good stdlib and the known-good set of dependencies the Fazm MCP servers need. Using the system Python would also risk writing .pyc files outside the app bundle and hitting a user's venv that has different dependency pins.
Is bundling Python legally and technically clean for a signed, notarized macOS app?
Yes. CPython is distributed under the PSF license, which is MIT-compatible and permits redistribution in a commercial product. The relocatable venv is produced by uv, which is Apache 2.0 licensed. libpython3.12.dylib is signed as part of the app bundle during the build because it is copied into the app bundle and then codesigned with the rest of Contents/Resources. PYTHONDONTWRITEBYTECODE=1 preserves the signature at runtime. The path the agent is told to use sits inside Contents/Resources, which is where Apple expects third-party payloads to live.
Can the bundled Python import pandas, requests, beautifulsoup, or other data-wrangling libraries?
Only if they are in the Google Workspace MCP's pyproject.toml dependency list at build time. build.sh line 211 reads pyproject.toml with tomllib and passes its dependencies to 'uv pip install'. The project currently ships MCP, google-auth, google-api-python-client, and the libraries those pull in. For a Python MCP server that wants pandas, the user adds it as a user MCP at ~/.fazm/mcp-servers.json with the command pointing at their own venv's python, and the bundled Python stays untouched. That separation is deliberate: the bundled Python is a product surface, not a general-purpose user-facing REPL.
More on Mac-native automation and how the pieces fit
Neighboring guides
Web browser automation tool that runs inside your real Chrome
The other half of the story: how Fazm attaches to your live Chrome via the Playwright MCP Bridge extension, with a glowing overlay on every driven page.
Accessibility API vs screenshot agents
Why Fazm reads labelled DOM and AXUIElement trees instead of asking a vision model to guess pixel coordinates from a screenshot.
AI automation for small business getting started
What the no-Python-install path looks like in practice for a non-developer owner who just wants their SaaS tools to talk to each other.
Comments (••)
Leave a comment to see what others are saying.Public and anonymous. No signup.