Production-grade ready template for Pi-powered browser extensions
pi-browser-template is a small starter project for connecting a Pi extension to a browser extension through a native messaging host.
It supports Linux, macOS, and Windows.
The template is end-to-end type safe: Pi, the native host, and the browser extension all share validated message types.
The included example lets Pi show an alert() in the active browser tab.
├── apps/ # Main runnable applications
│ ├── browser-extension/ # WXT-based browser extension: background script, popup UI, icons, messaging utils
│ ├── native-host/ # Native messaging host that connects the browser extension to the local system
│ └── pi/ # Pi/client-side app or library for defining commands, tools, and host communication
│
├── packages/ # Shared internal packages used by the apps
│ ├── env-wxt/ # Environment/config helpers for WXT projects
│ ├── native-messaging-schemas/ # Shared message schemas between browser, native host, and Pi
│ ├── typescript-config/ # Shared TypeScript configuration presets
│ └── wxt/ # Shared WXT utilities/configuration
│
├── AGENTS.md # Notes/instructions for AI agents or contributors
├── CHANGELOG.md # Release history
├── LICENSE # Project license
├── README.md # Main project documentation
├── package.json # Root package scripts and workspace metadata
└── pnpm-workspace.yaml # pnpm workspace package definitions
apps/pi — Pi command and tool.apps/browser-extension — WXT Chrome/Chromium extension.apps/native-host — native messaging host that connects Chrome to Pi.packages/native-messaging-schemas — shared TypeBox message schemas and types.Chrome needs a stable extension key in development. This keeps your extension ID the same after reloads.
From the repository root, run:
mkdir -p .keys
openssl genrsa -out .keys/chrome-extension.pem 2048
openssl rsa -in .keys/chrome-extension.pem -pubout -outform DER | openssl base64 -A
Copy the long value printed by the last command. It should look like:
MIIBIjANBgkqhkiG9w0BAQEFA...
.env filesOpen apps/browser-extension/.env.example and set:
# paste-your-key-here
WXT_CHROME_EXTENSION_KEY="MIIBIjANBgkqhkiG9w0BAQEFA..."
WXT_NATIVE_HOST_NAME=pi_native_bridge
Create the env files before installing dependencies. pnpm install runs WXT's postinstall prepare step, which validates these variables.
cp apps/browser-extension/.env.example apps/browser-extension/.env
cp apps/browser-extension/.env apps/native-host/.env
pnpm install
Run:
pnpm dev
WXT will start the extension in development mode. Keep this terminal open.
chrome://extensions.It is usually one of these:
apps/browser-extension/.output/chrome-mv3
dist/chrome-mv3
In a second terminal, run:
NODE_ENV=development pnpm native-host:setup
# OR
NODE_ENV=production pnpm native-host:setup
This installs the native messaging manifest for your browser and allows your extension ID to connect.
If Chrome was already open, fully restart Chrome after this step.
Open the extension popup in Chrome. It should show whether the native host is connected.
From the parent folder of this repository, run:
pi --no-extensions -e ./
Restart or reload Pi after installing.
This project registers:
alert-browseropen_browser_alertTry this in Pi:
Run the open_browser_alert tool with the message "Hello from Pi"
Or run the command:
/alert-browser Hello from Pi
pnpm dev # start the browser extension in dev mode
pnpm native-host:setup # build and install the native host
pnpm build # build all workspaces
pnpm typecheck # typecheck all workspaces
pnpm clean # remove generated files
WXT_CHROME_EXTENSION_KEY must be used by the browser extension and native host setup.chrome://extensions cannot run injected scripts. Test on a normal web page.pnpm native-host:setup again.MIT
$ claude mcp add pi-browser-template \
-- python -m otcore.mcp_server <graph>