Local Review MCP
by summeredge
README.md
# Local Review MCP
## Current version
V0.1 Release Candidate / Phase 5.6.1
## Current capabilities
- MCP Streamable HTTP runtime
- nineteen read-only tools, including the workspace registry, review context, and
interactive Session status tools
- `submit_goal` Control Plane entry point with asynchronous exact-current-conversation binding
- batch CLI and interactive Codex app-server Execution backends
- normalized interactive Session events with read-only Session/Execution status queries
- schema-validated MCP structured outputs and schema-v1 ReviewVerdict parsing
- durable Extension delivery and review-completion channels with identity-bound receipts
- MV3 Browser Extension/Fiber identity evidence and loopback Local Control Bridge
- fixed loopback host
- configurable fixed port
- startup port conflict detection
- Bearer-token authentication for all MCP HTTP requests
- OAuth 2.1-compatible discovery, public-client registration, PKCE, and Bearer tokens
- safe `GET /health` endpoint
- Cloudflare Tunnel provider with managed lifecycle
- a registry of authorized workspaces with legacy single-workspace compatibility
- bounded workspace metadata, directory listing, and text-file reading
- bounded literal or regular-expression search with ripgrep and Node fallback
- read-only structured Git status and bounded diff review
- optional Windows Supervisor with health monitoring and bounded recovery
- optional Windows Tray status/actions and per-user startup registration
- Remote MCP protocol, authentication, workspace-review, and restart E2E tests
## Architecture
The Local MCP Server keeps the MCP Data Plane and the Control Plane separate:
```text
MCP Read-only Data Plane
Workspace / Git / Review Context / status / hash-only diagnostics
MCP Control Plane entry
submit_goal
|
v
Pending Goal identity gate -> Goal -> Phase -> Task
|
v
Controlled Actuation -> Execution
```
The MCP server exposes nineteen read-only tools and the reviewed `submit_goal`
entry point. Browser Extension/Fiber, Local Control Bridge, Dispatcher, Codex
execution, and Review Loop work remain Control Plane capabilities; MCP does not
provide general file-write, shell, commit, push, or agent-control operations.
Execution and review use these current paths:
```text
batch: Task -> Execution (Codex CLI)
interactive: Task -> Session -> provider Thread -> Turn -> Execution -> normalized Events
passed Execution -> Review Request -> Conversation Routing -> Review Delivery
-> Extension Delivery -> Extension Review Completion -> ReviewResult
-> ReviewVerdict -> AutoIteration -> complete | next Execution | human required
```
Request correlation (`correlation_key -> conversation_id`) is identity evidence
only; `ConversationRouting.conversation_id` remains the authoritative Review
Delivery target.
`Session` is the long-lived interactive context and stores the provider Thread
ID. `Turn` is the provider-level unit for one interactive Execution. Batch
execution continues through `codex exec --json -` and does not automatically
create a Session. The Playwright Browser Worker remains an independent automation
path for explicit diagnostics and compatibility; current integrated delivery and
completion use the Extension/Bridge path, and the Worker is not an MCP Data Plane
capability.
Completed ReviewResults are parsed as schema-v1 `ReviewVerdict` values; the
integrated `AutoIteration` path handles `APPROVE`, `ITERATE`, and
`HUMAN_REQUIRED` outcomes.
### Interactive Execution and status
`submit_goal` supports `execution_mode: "batch" | "interactive"`, defaulting to
`batch`, plus optional provider `model` and `reasoning_effort`. Interactive
Execution uses the Codex app-server backend, discovers the provider catalog with
`model/list`, creates a Session and Thread, starts a Turn, and persists only
normalized LRM events. It does not silently select the first model.
The Session status model is `created`, `starting`, `active`, `running_turn`,
`waiting_input`, `completed`, `failed`, or `terminated`. The currently wired
interactive path uses `created -> starting -> active -> running_turn ->
completed|failed`; approval, user input, pause, and resume scheduling are not
implemented. The compatible Execution status remains `running`, `passed`, or
`failed`.
The read-only status tools are:
- `get_session_status` by `session_id` or `goal_id`, including Session, Thread,
model/effort, Goal, and current Execution status;
- `get_execution_status` by `execution_id`, including proven Session/Thread/Turn
association and bounded agent output;
- `list_session_events` with ordered, paginated normalized events. Provider
JSON-RPC payloads are not exposed.
## Default endpoint
`http://127.0.0.1:12080/mcp`
Start the server with:
```text
npm install
npm start -- --workspace <path> --token <token>
```
The port can be overridden with `--port <number>`. The token can also be
provided through `LOCAL_REVIEW_MCP_TOKEN`. Token precedence is CLI, environment,
then config file. A JSON config file can be provided with `--config <path>`:
```json
{
"port": 12080,
"workspace": {
"id": "project",
"name": "Project",
"path": "C:\\path\\to\\project"
},
"workspaces": [
{
"id": "project",
"name": "Project",
"path": "C:\\path\\to\\project"
}
],
"auth": { "token": "<token>" },
"remote": { "enabled": false, "endpoint": "" },
"supervisor": {
"enabled": false,
"healthIntervalSeconds": 30,
"maxRestartAttempts": 3
}
}
```
`workspaces` is optional for legacy configurations. When present, its entries
are the only workspaces that MCP can select; the top-level `workspace` identity
selects the active entry and is checked against the registry. A legacy string
`workspace` remains supported for direct single-workspace startup.
Set `supervisor.enabled` to `true` to run the MCP runtime under the Windows
Supervisor. It checks `/health` at the configured interval, performs at most
`maxRestartAttempts` automatic restarts, and exposes Start, Stop, Restart,
Open Log Folder, startup registration, and Exit from the Tray menu. Supervisor
logs are stored under the user's local application data directory and contain
only fixed lifecycle events.
## Production Deployment
The supported deployment path is Windows → Local Review MCP → Cloudflare Tunnel
→ ChatGPT Web custom MCP connector.
### Windows requirements
- Windows PowerShell 5.1 or PowerShell 7.
- Node.js with npm available as `node --version` and `npm --version`.
- `cloudflared` on `PATH` when `remote.enabled` is `true`. Install it from the
[Cloudflare tunnel documentation](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/).
- A workspace directory that already exists and is readable. The deployment
scripts never create it.
From the repository root, install dependencies and build the release runtime:
```powershell
npm install
npm run build
```
Prepare the local production configuration. The example contains no real token,
endpoint, or workspace:
```powershell
Copy-Item .\config.production.example.json .\config.production.json
```
Edit `config.production.json` and set `workspace` to the project directory the
connector may review. Set `remote.tunnelName` to the existing Cloudflare Named
Tunnel name or UUID and `remote.endpoint` to its stable public HTTPS `/mcp`
endpoint. Keep `config.production.json` local; `.gitignore` excludes it.
Set the bearer token in the process environment before starting. It is not
stored in the repository:
```powershell
$env:LOCAL_REVIEW_MCP_TOKEN = "<long-random-review-token>"
```
Make sure `cloudflared` is installed. The production Supervisor runs the
configured Named Tunnel with its local credentials file. If the installation
uses a tunnel token instead, set `CLOUDFLARE_TUNNEL_TOKEN` for that same
configured tunnel. Do not put either token in source control.
Run the standard startup entry point:
```powershell
.\scripts\start-production.ps1 -Config ".\config.production.json"
```
The entry point reads the configuration, runs `preflight-check.ps1`, then
starts the existing Local Review MCP runtime. The runtime starts the Windows
Supervisor when `supervisor.enabled` is `true` and starts the Cloudflare Tunnel
when `remote.enabled` is `true`. It prints the local health endpoint and any
ready remote endpoint without printing credentials.
The preflight check verifies Node/npm, installed dependencies, required config
sections, workspace access, and port availability. A missing `cloudflared` is
fatal only when remote access is enabled. It reports an occupied port and asks
you to change `port`; it never auto-installs dependencies, creates a workspace,
or selects another port.
### Remote verification
After the tunnel reports a public endpoint, verify the deployment from a PowerShell
process that has the remote URL and token:
```powershell
$env:LOCAL_REVIEW_MCP_REMOTE_URL = "https://<public-hostname>/mcp"
$env:LOCAL_REVIEW_MCP_REMOTE_TOKEN = $env:LOCAL_REVIEW_MCP_TOKEN
.\scripts\verify-remote.ps1
```
`verify-remote.ps1` checks that unauthenticated and wrong-token health requests
return HTTP 401, the correct token returns `status=ok`, MCP `initialize` works,
and `tools/list` matches its embedded tool allowlist. The current runtime
advertises nineteen read-only tools plus `submit_goal` (20 registered tools);
this script still contains the older fifteen-tool allowlist, so its final
comparison is not a complete current-surface check. The remote test suite uses
the current registered-tool fixture for that check.
## Remote MCP Setup
Remote access is disabled by default. Enable the Cloudflare provider with:
```json
{
"remote": {
"enabled": true,
"provider": "cloudflare",
"tunnelName": "<tunnel-name-or-uuid>",
"endpoint": "https://<public-hostname>/mcp"
}
}
```
Start the local runtime with an explicit workspace and token:
```powershell
$env:LOCAL_REVIEW_MCP_TOKEN = "<long-random-review-token>"
npm start -- --workspace "C:\path\to\project" --config "settings.json"
```
Install `cloudflared` and configure an existing Named Tunnel. `remote.tunnelName`
must contain its name or UUID, and `remote.endpoint` must contain the stable
public HTTPS `/mcp` URL. The Supervisor runs `cloudflared tunnel run` for that
tunnel and never creates a tunnel, changes DNS, or requests a temporary URL.
```powershell
$env:LOCAL_REVIEW_MCP_TOKEN = "<long-random-review-token>"
# Optional when using token-based credentials for the configured Named Tunnel.
$env:CLOUDFLARE_TUNNEL_TOKEN = "<cloudflare-tunnel-token>"
npm start -- --workspace "C:\path\to\project" --config "settings.json"
```
The Cloudflare provider invokes only the installed `cloudflared` executable,
does not upload configuration, and never logs credentials.
The live endpoint is returned by the provider and owned by `TunnelManager`;
`remote.tunnelName` and `remote.endpoint` are configuration inputs. No module
hardcodes a tunnel hostname.
### ChatGPT Web connector
1. In ChatGPT Web, enable the workspace's developer/custom-app capability if
the plan requires it, then open the Apps/Connectors settings and create a
custom MCP app.
2. Enter the HTTPS endpoint reported by `TunnelManager` or the authenticated
`/health` response, using the `/mcp` path.
3. Select the connector's OAuth authentication option. The server publishes
MCP protected-resource metadata, authorization-server metadata, dynamic
client registration, and PKCE endpoints under the same public origin.
4. Scan the tools, confirm the read-only actions plus `submit_goal`, save the draft app, and
select it from a new chat. Ask for a code review; ChatGPT should call
`workspace_list` first, then `review_summary`, `execution_output`,
`workspace_info`, `git_status`, `git_diff`, `read_file`, and `search_text`
with a `workspace_id` when selecting a registered workspace.
### Connector and browser readiness
For a real `submit_goal` handoff, keep the runtime, Tunnel (when remote access
is enabled), Bridge, and the exact ChatGPT connector available. Run the local
diagnostic after startup:
```powershell
npm run diagnose:chatgpt-connector -- --config <config>
```
If the exact connector has just been created or adopted, call `workspace_info`
through that exact OAuth connector and confirm it with the returned request ID:
```powershell
npm run confirm:chatgpt-connector -- --config <config> --request-id <workspace_info.request_id>
```
Continue only when the diagnostic reports `ok=true`, `connector.status=verified`,
`connector.action=none`, and ready remote/OAuth checks. The connector workflow
in [`docs/chatgpt-connector-workflow.md`](docs/chatgpt-connector-workflow.md)
covers exact-name checking, reauthorization, and adoption of an existing
ChatGPT connector. Confirmation evidence is OAuth-authenticated `workspace_info`
evidence for the current workspace and expires after ten minutes.
The unpacked MV3 extension is in `extension/`; it needs to be loaded manually
in Chrome or Edge and has no build step. After reloading the extension, refresh
the ChatGPT page so its content scripts are replaced, then use a concrete
conversation route rather than the New Chat root:
```text
https://chatgpt.com/c/<conversation_id>
https://chatgpt.com/g/<project>/c/<conversation_id>
```
The supported Project form is the one-segment route
`/g/<project>/c/<conversation_id>`. `fiber.js` runs in the MAIN world and emits
only bounded evidence from the current turn. For direct `submit_goal`, it
accepts only the exact assistant tool request for `api_tool.call_tool` or
`Local_MCP_Connector.submit_goal` whose `args` or `arguments` contains the
strict UUID v4 `correlation_key`. It never substitutes assistant text, tool
results, or `message.metadata.request_id` for that direct key. The content and
background scripts additionally require Fiber/URL conversation equality,
Chrome `MessageSender.documentId`, and the current `navigation_epoch`; stale,
conflicting, and new-chat identities produce no canonical evidence. A raw
`WEB:*` value is provisional and never becomes a canonical owner; only a valid
same-model `serverId$()` result can resolve it to a canonical conversation ID.
The Local Control Bridge is a separate loopback Control Plane server on
`127.0.0.1`, discovered on ports `12081` through `12085`, using protocol `3`.
It pairs one validated Extension Origin and authenticates protected identity,
delivery, and completion transport with a process-local bearer token.
`GET /hello` is discovery; protected identity evidence is posted to
`/identity-evidence`. If the Bridge is unavailable, the MCP Data Plane can still
start, but `submit_goal` cannot pass its Browser identity-channel readiness gate.
`GET http://127.0.0.1:<port>/launcher/readiness` is a loopback,
static-token-protected status endpoint also shown by the Launcher. `ready` means
the Bridge is available, the Extension is paired, and Extension presence was
seen within the ten-second presence window. Presence is only channel
readiness, not conversation proof. The Launcher may briefly retain a recent
READY display during its presentation grace period, but `submit_goal` still
checks live readiness and requires the exact Fiber/URL/document/epoch evidence
above.
`submit_goal` does not accept `conversation_id`. The model must generate a fresh
UUID v4 `correlation_key` for every call. The call first returns a durable
acceptance receipt while a `PendingGoalSubmission` waits for matching canonical
evidence (default TTL: two minutes); only then does Goal startup happen
asynchronously. `accepted` is not Goal, Session, Execution, or review
completion. If the key is not matched before expiry, no Goal is created. The
read-only `get_identity_trace` and `get_evidence_transport_trace` tools expose
hash-only, correlation-key-scoped diagnostics and never return raw payloads,
tokens, cookies, or message text.
After identity matching, Goal preflight still checks runtime readiness, the
active registered workspace identity, and verified Connector/OAuth/remote
readiness; Browser presence alone never authorizes a Goal.
The exact ChatGPT Web menu labels and availability depend on the workspace
plan. The MCP endpoint itself is `/mcp`; `/health` is an authenticated
readiness check. OpenAI's current [MCP and Connectors guide](https://developers.openai.com/api/docs/guides/tools-connectors-mcp)
describes remote MCP server URLs and tool approval configuration.
At least one workspace is required. With a registry, the first entry is the
legacy active workspace unless the top-level `workspace` matches another
registered path. Without `workspace_id`, tools use that active workspace.
The current nineteen read-only tools are `workspace_info`, `list_files`,
`read_file`, `search_text`, `git_status`, `git_diff`, `workspace_get_info`,
`workspace_list_files`, `workspace_read_file`, `workspace_search`,
`workspace_review_context`, `workspace_list`, `review_summary`,
`execution_output`, `get_session_status`, `get_execution_status`,
`list_session_events`, `get_identity_trace`, and `get_evidence_transport_trace`.
The Control Plane tool `submit_goal` durably accepts a Goal and starts it after
canonical Extension evidence proves the conversation. The five explicitly
scoped `workspace_*` review tools require `workspace_id`; the other
workspace-scoped tools preserve optional active-workspace behavior, while
`workspace_list` has no workspace selector.
Git tools are bound to the selected registered workspace, do not expose Git
command arguments, and never perform write operations. `workspace_list` returns
only each workspace's stable `id` and display `name`, never its local path.
`review_summary` combines workspace metadata with Git status and diff counts.
`execution_output` only reads `.review/execution_output.json` and returns
`{"available":false}` when that file is absent; it never runs the recorded
command or accepts a file path.
`submit_goal` requires a fresh `correlation_key` and accepts an optional
`workspace_id`, `title`, `goal`,
`requirements`, `acceptance_criteria`, `max_iterations` (default `2`),
`execution_mode` (default `batch`), `model`, and `reasoning_effort`. Its
`conversation_id` is resolved internally from the exact Extension evidence
chain; it is not caller-supplied.
The health endpoint requires the configured static `Authorization: Bearer <token>`
even on localhost and through Cloudflare Tunnel. MCP requests accept either that
legacy token or an OAuth access token. The health endpoint is
`http://127.0.0.1:<port>/health`; it returns the status, stable workspace
identifier, version, `remote_status`, `endpoint_status`, the public endpoint
only when it is ready, and an `oauth_registry` summary. The health summary uses
the relative `oauth/clients.json` location so it does not disclose local paths.
The loopback-only, static-token-protected `GET /oauth/clients` endpoint returns
the full registry path, load state, client count, and non-secret client summaries.
`DELETE /oauth/clients/<client_id>` removes one registration and
`DELETE /oauth/clients` clears registrations without deleting the registry file.
The health endpoint never returns tokens, credentials, local IPs, or workspace
absolute paths.
`search_text` accepts an optional `workspace_id`, `query`, workspace-relative
`path` and `glob`, `regex`, `case_sensitive`, and `limit`. Searches are
restricted to allowed text files up to 2 MiB, with at most 200 returned results
and 500 preview characters per result.
## E2E verification
The remote suite uses the same Streamable HTTP MCP client flow as a remote
connector and covers `initialize`, `tools/list`, workspace registry selection,
ordered workspace review,
Bearer authentication, safe health metadata, sensitive-file denial, traversal/
absolute/drive/symlink path denial, and tunnel stop/restart. It creates a
temporary `sample-project` Git workspace and never commits review changes.
Run the release-candidate checks locally:
```text
npm run typecheck
npm test
npm run build
```
The Browser Worker review-submission diagnostic drives the complete local
Review Delivery chain with a mock Page and does not require a ChatGPT login:
```powershell
npm run diagnose:review-submission
```
The live Goal E2E diagnostic exercises the Extension-based Goal, delivery,
review-completion, verdict, and terminal-state path for one concrete ChatGPT
conversation:
```powershell
npm run diagnose:goal-e2e -- --config config.production.json --conversation-id <conversation_id>
```
It requires the exact connector, an open target conversation, and a paired and
present Extension. The automated Phase 5.6 review-loop test uses an in-process
app-server test double; it verifies LRM lifecycle and verdict parsing, not live
ChatGPT/browser delivery.
The interactive app-server smoke uses a temporary state root and requires the
requested provider selection:
```powershell
$env:CODEX_INTERACTIVE_MODEL = "gpt-5.6-luna"
$env:CODEX_INTERACTIVE_EFFORT = "max"
npm run test:interactive-goal
```
To probe an already deployed HTTPS endpoint with the optional remote test,
provide `LOCAL_REVIEW_MCP_REMOTE_URL` and
`LOCAL_REVIEW_MCP_REMOTE_TOKEN` only in the process environment before running
the remote test. No token or public URL is stored in the repository.
## LocalReviewLauncher
[`LocalReviewLauncher/README.md`](LocalReviewLauncher/README.md) describes the
independent PySide6 Windows launcher. `start-launcher.cmd` starts the existing
`scripts/start-production.ps1` entry point; the launcher does not replace or
embed the MCP runtime. Its startup and status views cover Start/Stop/Refresh,
Workspace Registry, configuration validation/open/backup, OAuth client status,
MCP health, Tunnel/remote status, and Browser readiness through authenticated
local HTTP checks.
The read-only Task Dashboard lists only interactive `codex_app_server` Sessions
and shows Goal/Task, Session/Thread, model/effort, status, current Execution,
and last update. The Session Viewer shows the current Execution and the paginated
normalized Event Stream; adjacent agent-message deltas are aggregated for display
and the rendered stream is capped at 500 rows. It refreshes every five seconds
and provides an Open Codex Task locator using the proven Thread/Session IDs. The Launcher only
observes lifecycle state and does not submit, stop, resume, approve, or control
a Goal, Execution, Thread, or Turn. Separate cleanup actions can clear the
display or remove completed/failed/terminated persisted task records; running
Sessions are retained.
## Security Notes
Local Review MCP provides `read`, `search`, `review`, and interactive status
capabilities through its nineteen read-only tools, plus the reviewed `submit_goal` Control Plane entry
point. It does not provide:
- `modify` or `write_file` operations;
- `execute` or shell operations;
- direct `agent` or Codex/ChatGPT automation controls outside `submit_goal`.
Keep `LOCAL_REVIEW_MCP_TOKEN`, `CLOUDFLARE_TUNNEL_TOKEN`, and
`LOCAL_REVIEW_MCP_REMOTE_TOKEN` in the process environment or another local
secret store. Never commit tokens, `.env` files, Cloudflare credentials,
private keys, or `config.production.json`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues