Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ on:

env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
# tsup's --dts worker hits Node's default ~2GB heap during the mcp-server
# build (70+ entry points + DTS emission). Lift the cap to 4GB; both
# ubuntu-latest and windows-latest runners have ~7GB RAM available.
NODE_OPTIONS: "--max-old-space-size=4096"

jobs:
build-and-test:
Expand All @@ -26,6 +30,19 @@ jobs:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
# Workaround for npm/cli#4828: optional native bindings sometimes don't
# install when `npm ci` runs from a lockfile generated on a different
# platform. Force-install the @tailwindcss/oxide native binding for the
# current runner so vite/postcss can find it.
- name: Install platform-specific tailwind oxide binding (npm/cli#4828)
run: |
if [ "$RUNNER_OS" = "Linux" ]; then
npm install --no-save --workspaces=false @tailwindcss/oxide-linux-x64-gnu
elif [ "$RUNNER_OS" = "Windows" ]; then
npm install --no-save --workspaces=false @tailwindcss/oxide-win32-x64-msvc
elif [ "$RUNNER_OS" = "macOS" ]; then
npm install --no-save --workspaces=false @tailwindcss/oxide-darwin-arm64 @tailwindcss/oxide-darwin-x64
fi
- run: npm run build
- run: npm run typecheck
- name: Test with coverage
Expand All @@ -52,7 +69,21 @@ jobs:
cd packages/extension
npm run prepare:package-deps || true
npx @vscode/vsce ls --no-dependencies 2>&1 | tee /tmp/vsix.txt
if grep -qE "\.ts$|dev-tools" /tmp/vsix.txt; then
echo "ERROR: Source files would leak into VSIX!"
# Match true source .ts files (foo.ts) in OUR package directories
# but NOT (a) type declarations (foo.d.ts) bundled with their .js
# by npm packages, (b) any .ts under a node_modules/ path —
# vendored deps like patchright-core legitimately ship their own
# .ts source files alongside the compiled .js. The check is
# strictly about preventing OUR src/ from leaking, not vendored
# third-party content.
if grep -E "\.ts$" /tmp/vsix.txt | grep -vE "\.d\.ts$" | grep -vE "node_modules/" | head -1 | grep -q .; then
echo "ERROR: Source .ts files would leak into VSIX!"
grep -E "\.ts$" /tmp/vsix.txt | grep -vE "\.d\.ts$" | grep -vE "node_modules/" | head -10
exit 1
fi
# dev-tools is under our own packages; never legitimate.
if grep -qE "dev-tools" /tmp/vsix.txt; then
echo "ERROR: dev-tools path would leak into VSIX!"
grep -E "dev-tools" /tmp/vsix.txt | head -10
exit 1
fi
132 changes: 132 additions & 0 deletions docs/codex-cli-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Codex CLI setup for Perplexity MCP

Operator guide for connecting [Codex CLI](https://github.com/openai/codex) to the Perplexity MCP server. Codex CLI reads MCP servers from `~/.codex/config.toml`. Two transports are supported: HTTP (recommended, routes through the extension daemon) and stdio (standalone Node launcher).

Motivation: a real-world Linux setup hit the locked-vault failure mode — Codex CLI's standalone Node launcher had no TTY, no libsecret/gnome-keyring, and no `PERPLEXITY_VAULT_PASSPHRASE`, so the encrypted profile vault could not be unsealed and Pro-tier tools failed even though the install was otherwise healthy. The structural fix that makes the HTTP transport the default for Codex CLI shipped in commit `895b04d` (`feat(auto-config): enable http-loopback for Codex CLI with TOML bearer env headers`).

---

## 1. TL;DR — recommended path

**Run "Perplexity: Configure for All" from the VS Code extension.** That command writes the HTTP-transport block into `~/.codex/config.toml`. Codex CLI then makes bearer-authenticated HTTP calls to the extension-managed daemon, which has SecretStorage access and unseals the vault on its own. **No keychain, passphrase, or TTY is needed in the Codex CLI subprocess.**

The block the extension writes for Codex CLI looks like this:

```toml
[mcp_servers.Perplexity]
url = "http://127.0.0.1:<port>/mcp"
bearer_token_env_var = "PERPLEXITY_MCP_BEARER"
enabled = true

[mcp_servers.Perplexity.env_http_headers]
PERPLEXITY_MCP_BEARER = "<rotating-bearer-from-extension>"
```

Notes:

- The env-var name is derived from the server name. For `Perplexity` the extension generates `PERPLEXITY_MCP_BEARER` (`<UPPER_SERVER_NAME>_MCP_BEARER` with non-alphanumerics collapsed to `_`).
- `<port>` and `<rotating-bearer-from-extension>` come from the daemon's `daemon.lock` and `daemon.token` files in `~/.perplexity-mcp/` (or `$PERPLEXITY_CONFIG_DIR`). Re-running "Configure for All" refreshes both if they change.
- Restart Codex CLI after running "Configure for All" so it picks up the new config.

---

## 2. Stdio transport — manual setup with one of three auth options

Use this when you cannot run the extension daemon — for example, Codex CLI on a headless server without VS Code installed. The stdio block:

```toml
[mcp_servers.Perplexity]
command = "/usr/bin/node"
args = ["/home/<user>/.perplexity-mcp/start.mjs"]
enabled = true

[mcp_servers.Perplexity.env]
PERPLEXITY_HEADLESS_ONLY = "1"
# pick one auth option below
```

Use `node` (without an absolute path) only if it is on the Codex CLI process's PATH. Do **not** point `command` at `Code.exe`, `Cursor.exe`, `Electron`, `windsurf-next`, or any other Electron host — those binaries spawn a UI process and the launcher will not run as a Node script.

Pick one of the following auth options.

### 2a. OS keychain (recommended for desktop Linux)

```bash
sudo apt install libsecret-1-0 gnome-keyring # Debian/Ubuntu
sudo dnf install libsecret gnome-keyring # Fedora
chmod 700 ~/.perplexity-mcp # tighten profile dir perms
```

Then run `perplexity_login` once via the extension or the standalone CLI to seed the keychain. Subsequent MCP-server starts unseal silently via the `tryKeytar` path.

### 2b. Passphrase env var (works without a keychain)

```toml
[mcp_servers.Perplexity.env]
PERPLEXITY_HEADLESS_ONLY = "1"
PERPLEXITY_VAULT_PASSPHRASE = "<your-passphrase>"
```

Security caveat: the passphrase is stored as plaintext in `~/.codex/config.toml`. Acceptable on a single-tenant machine when the file is `chmod 600`; not acceptable on a shared host or any system where other users can read your home directory.

### 2c. Use the HTTP transport instead

If you have the extension installed, prefer section 1 — the daemon owns the vault and Codex CLI sees only a bearer-authed HTTP endpoint.

---

## 3. Per-platform notes

### Linux

- libsecret + gnome-keyring may not be installed by default on server distros. Sections 2a and 2b cover both cases.
- The VS Code extension uses VS Code SecretStorage, which on Linux delegates to the same libsecret backend that the standalone CLI's `tryKeytar` path uses. If keychain works for the extension, it will work for the standalone launcher (after installing the libsecret packages in the Codex CLI environment).
- Doctor reports `Config dir is world/group readable (mode 0775)` when perms are loose — fix with `chmod 700 ~/.perplexity-mcp`.

### macOS

- Keychain Access is always available. Section 2a "just works" with `tryKeytar` after a one-time `perplexity_login`.

### Windows

- Credential Manager is always available. Same as macOS: section 2a works after a one-time `perplexity_login`.

---

## 4. Verifying the setup

After configuring, run these three checks:

1. From Codex CLI, list MCP servers and confirm `Perplexity` appears with `enabled = true`.
2. Invoke the `perplexity_doctor` tool from Codex CLI. The `vault` check must report `pass` and `unseal-path` must show which path resolved (`keychain`, env var, or passphrase).
3. Invoke `perplexity_search` with a simple query. If results come back with citations, the chain works end-to-end.

---

## 5. Troubleshooting

### `Vault locked: no keychain, no env var, no TTY`

The Codex CLI subprocess could not unseal the vault. Pick one of the auth options in section 2, or switch to the HTTP transport in section 1.

### `command path is wrong-runtime` (from doctor)

`command` in `~/.codex/config.toml` points at an Electron host (Code.exe, Cursor.exe, windsurf-next, etc.), not at a Node binary. Set `command = "node"` (or an absolute path to a Node binary) and re-run "Configure for All" in the extension.

### `Auth: Unsupported` shown by Codex CLI

Cosmetic. It means the MCP server does not advertise MCP-level OAuth to Codex CLI. Perplexity uses bearer auth on the HTTP transport, not OAuth, so this label is expected and does not indicate a setup error.

### Pro-tier features missing despite a Pro account

Re-login. The fix for ASI/computer-access tier inference shipped in commit `2d287c6` (`fix(login): infer Pro tier from ASI computer access`); older sessions may still be tagged as Free until the cookie is refreshed.

---

## 6. Reference — what each transport does

| Transport | Spawned by Codex CLI? | Vault unseal | Setup complexity |
|---|---|---|---|
| HTTP (recommended) | No — uses extension daemon | Daemon handles it | Low (one click in extension) |
| stdio + keychain (2a) | Yes — Node subprocess | `tryKeytar` (libsecret) | Medium (install libsecret) |
| stdio + passphrase (2b) | Yes — Node subprocess | env-var passphrase | Low (but plaintext on disk) |
163 changes: 163 additions & 0 deletions docs/smoke-evidence/2026-04-28-codex-cli-toml-loopback-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Codex CLI TOML bearer-env http-loopback — smoke evidence (TEMPLATE)

> Status: **TEMPLATE — UNFILLED.** No checkbox below has been verified. Operator
> must edit this file in-place when actually running the smoke. Do NOT cite this
> file as evidence until at least one OS section is signed off.

## Why this addendum exists

The 2026-04-24 evidence doc (`2026-04-24-http-loopback-static-bearer.md`)
records the JSON-shaped `headers.Authorization: "Bearer <token>"` http-loopback
shape used by Cursor, Claude Desktop, Claude Code, Cline, Windsurf, Windsurf
Next, Amp, Roo Code, Continue.dev, and Zed. It does **not** cover Codex CLI:

- Codex CLI consumes `~/.codex/config.toml`, not `mcp.json`.
- Codex CLI does not accept a literal bearer in `[mcp_servers.<name>]`. Bearer
must be referenced indirectly via `bearer_token_env_var` and the actual value
set in `[mcp_servers.<name>.env_http_headers]`.
- The shape was added in commit `895b04d` (2026-04-26),
*after* the 2026-04-24 doc was written — so referencing the older doc as
evidence for `codexCli.httpBearerLoopback` is structurally wrong.

This template captures the per-OS smoke needed to back the Codex CLI claim.

## Front-matter

- **Date:** 2026-04-XX (operator fills)
- **Operator:** <name>
- **Platform:** <Linux distro / macOS version / Windows version>
- **Codex CLI version:** <version, e.g. output of `codex --version`>
- **Extension version:** <e.g. perplexity-vscode-0.8.10.vsix>
- **Daemon port:** <port from `~/.perplexity-mcp/daemon.lock`>

## Setup performed

1. Install the VSIX from a clean profile (or note the prior state).
2. Open VS Code with the extension installed at the version above.
3. Open the dashboard, enable the daemon, note the port and the active bearer.
4. From the command palette, run **"Perplexity: Configure for All"** (or the
per-IDE action for `codexCli` from the IDEs tab with transport
`http-loopback`).
5. Verify it writes `~/.codex/config.toml` (Windows: `%USERPROFILE%/.codex/config.toml`)
with the HTTP-transport TOML shape shown below.
6. Restart Codex CLI; verify `Perplexity` appears in its MCP server list with
`enabled = true` and reports as authenticated (no `Auth: Unsupported` for
the loopback bearer path — that warning is a known cosmetic display for the
stdio launcher and is documented in `linux/perplexity-codex-mcp-setup-issue.md`).
7. List MCP tools — confirm `perplexity_search`, `perplexity_doctor`,
`perplexity_models`, `perplexity_research`, `perplexity_compute` appear.

## Expected TOML on disk

The auto-config writer (`buildTomlMcpBlock` in
`packages/extension/src/auto-config/index.ts`) emits the following exact shape
when given an `http-loopback` server config (an object with a `url` key and
`headers.Authorization = "Bearer <token>"`). The env var name is derived as
`<SERVERNAME>_MCP_BEARER` with non-alphanumerics collapsed to `_`. For server
name `Perplexity` this yields `PERPLEXITY_MCP_BEARER`.

```toml
[mcp_servers.Perplexity]
url = "http://127.0.0.1:<daemon-port>/mcp"
bearer_token_env_var = "PERPLEXITY_MCP_BEARER"
enabled = true

[mcp_servers.Perplexity.env_http_headers]
PERPLEXITY_MCP_BEARER = "<daemon-static-bearer>"
```

Notes:
- No `command`/`args` keys appear when `url` is set — that branch is exclusive.
- No `[mcp_servers.Perplexity.env]` block is written for the loopback transport.
(`env` is reserved for the stdio-launcher transport, where things like
`PERPLEXITY_HEADLESS_ONLY` belong.)
- The bearer is a literal value in `env_http_headers`. Codex CLI reads it at
spawn time; rotating the bearer requires the file to be re-written and Codex
to re-spawn the MCP child (see "Bearer rotation check" below).

Operator: paste the actual TOML written on disk here for the run, and confirm
it matches the shape above.

```toml
<paste actual ~/.codex/config.toml [mcp_servers.Perplexity] block here>
```

## Smoke checks — Linux (Ubuntu 22.04 / Fedora 40 / Arch — pick one)

Distro tested: <fill>

- [ ] `~/.codex/config.toml` contains the exact `[mcp_servers.Perplexity]` shape above
- [ ] Codex CLI lists Perplexity MCP server as `enabled`
- [ ] `perplexity_doctor` returns OK; `vault` check `pass`
- [ ] `perplexity_search` returns results with citations
- [ ] `perplexity_models` returns the right tier (Pro/Max)
- [ ] `perplexity_research` with a simple prompt returns a research result
- [ ] `perplexity_compute` with a file-producing prompt; verify file appears in `~/.perplexity-mcp/downloads/<slug>/`

### Bearer rotation check (Linux)

- [ ] After running the extension command "Rotate bearer", confirm the
`env_http_headers` block in `~/.codex/config.toml` updates AND Codex CLI's
next call still authenticates without manual restart of Codex itself
(or, document the restart requirement here if one is needed).

### Sign-off (Linux)

- [ ] All boxes above are checked (no extrapolation)
- Operator signature: <name> <date>

## Smoke checks — macOS 14+

- [ ] `~/.codex/config.toml` contains the exact `[mcp_servers.Perplexity]` shape above
- [ ] Codex CLI lists Perplexity MCP server as `enabled`
- [ ] `perplexity_doctor` returns OK; `vault` check `pass`
- [ ] `perplexity_search` returns results with citations
- [ ] `perplexity_models` returns the right tier (Pro/Max)
- [ ] `perplexity_research` with a simple prompt returns a research result
- [ ] `perplexity_compute` with a file-producing prompt; verify file appears in `~/.perplexity-mcp/downloads/<slug>/`

### Bearer rotation check (macOS)

- [ ] After "Rotate bearer", `env_http_headers` updates AND Codex's next call still authenticates

### Sign-off (macOS)

- [ ] All boxes above are checked (no extrapolation)
- Operator signature: <name> <date>

## Smoke checks — Windows 11

- [ ] `%USERPROFILE%/.codex/config.toml` contains the exact `[mcp_servers.Perplexity]` shape above
- [ ] Codex CLI lists Perplexity MCP server as `enabled`
- [ ] `perplexity_doctor` returns OK; `vault` check `pass`
- [ ] `perplexity_search` returns results with citations
- [ ] `perplexity_models` returns the right tier (Pro/Max)
- [ ] `perplexity_research` with a simple prompt returns a research result
- [ ] `perplexity_compute` with a file-producing prompt; verify file appears in `%USERPROFILE%/.perplexity-mcp/downloads/<slug>/`

### Bearer rotation check (Windows)

- [ ] After "Rotate bearer", `env_http_headers` updates AND Codex's next call still authenticates

### Sign-off (Windows)

- [ ] All boxes above are checked (no extrapolation)
- Operator signature: <name> <date>

## What was NOT tested by this addendum

- The stdio-launcher transport for Codex CLI (the `command`/`args` shape with
`[mcp_servers.Perplexity.env]`) — that is the Linux setup-issue subject of
`linux/perplexity-codex-mcp-setup-issue.md` and needs its own evidence doc
if/when the headless-vault path is signed off.
- `httpOAuthLoopback` for Codex CLI — not yet wired in `IDE_METADATA`.
- `httpOAuthTunnel` for Codex CLI — not yet wired in `IDE_METADATA`.

## Replay (operator quick-reference)

1. Install the VSIX.
2. Dashboard → enable daemon → note port.
3. IDEs tab → Codex CLI → transport `http-loopback` → Generate.
4. Inspect `~/.codex/config.toml` → confirm shape matches "Expected TOML on disk".
5. Restart Codex CLI → list MCP tools → run smoke checks above.
6. Rotate bearer from the dashboard → confirm config updates → re-run a tool call.
Loading
Loading