Skip to main content
Glama
Borge-Labs

claude-second-opinion-mcp

by Borge-Labs
README.md
# Claude Second Opinion MCP

[![CI](https://github.com/Borge-Labs/claude-second-opinion-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/Borge-Labs/claude-second-opinion-mcp/actions/workflows/ci.yml)
[![npm](https://img.shields.io/npm/v/claude-second-opinion-mcp.svg)](https://www.npmjs.com/package/claude-second-opinion-mcp)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

A focused local MCP server that lets Codex, or any MCP client, ask a fresh
Claude Code session for an independent second opinion without blocking the
calling agent.

It is designed for consequential uncertainty: plans, architecture, difficult
debugging, security/correctness risks, substantial code changes, and final
completion checks. It is not intended to become a mandatory ritual for routine
work.

## What makes it different

- `start_claude_review` returns immediately with a review ID.
- `get_claude_review` checks or retrieves the result later while the caller
  continues other work.
- `cancel_claude_review` terminates the Claude process group.
- Every review runs at `--effort max`.
- The model is configurable; the default `best` selector can use Fable when
  available, with Claude Code's native fallback to Opus.
- A zero-cost capacity failure that performed no model work gets one explicit
  fallback attempt within the original timeout and budget; work is never
  replayed after model usage, spend, or permission denials.
- The result reports the requested model plus every matching reviewer model in
  Claude's usage telemetry. The singular field is null when telemetry contains
  both the configured primary and fallback, avoiding guessed provenance.
- Denied tool calls are surfaced by name and force an `insufficient_context`
  verdict instead of allowing a falsely complete review.
- Claude output must match a strict review schema and pass consistency checks.
- Packet-only reviews remain tool-disabled and isolated from the workspace.
- Workspace-aware reviews can inspect a trusted repository with Claude Code's
  built-in tools and a configurable permission mode.
- The default authentication policy requires first-party Claude.ai Max login
  and strips alternate-provider overrides to avoid accidental API billing.
- Runtime, input, output, concurrency, and subprocess cleanup are bounded.

## Review modes

### Packet-only

Omit `workspace`. Claude receives only the supplied task, artifact, question,
and focus fields. Tools are disabled, safe mode is forced on, and the review
runs from a temporary directory.

This mode is useful for reviewing a plan, proposal, diagnosis, or draft answer
without granting repository access.

### Workspace-aware

Supply an absolute `workspace` path underneath one of the roots configured in
`CLAUDE_SECOND_OPINION_WORKSPACE_ROOTS_JSON`. The path is canonicalized and
validated before the background job is accepted. Claude starts in that
directory with normal built-in tools.

By default, workspace reviews use:

- `--safe-mode`
- `--permission-mode plan`
- a clean child-process environment
- no project `CLAUDE.md`, hooks, skills, plugins, or project MCP servers

This preserves an independent reviewer while still allowing Claude to inspect
files and diffs. Every effective execution profile is returned with the job
snapshot.

## Safety model

Safe mode, permission mode, and sandboxing solve different problems:

- **Safe mode** controls Claude Code customizations. It does not disable
  built-in tools and is not a sandbox.
- **Permission mode** controls which tool actions Claude Code authorizes.
- **OS sandboxing** controls what the subprocess can actually reach. A working
  directory or configured workspace root is not an OS boundary.

`bypassPermissions`, including the accepted
`--dangerously-skip-permissions` alias, disables Claude Code permission checks.
Anthropic recommends it only in externally isolated containers or VMs. In
that mode Claude can potentially read or modify files outside the selected
workspace, execute commands, use the network, invoke MCP tools, or expose
credentials available to the OS user.

The reviewer system prompt prohibits intentional edits, deployments,
publishing, contacting people, and external state changes. A prompt is not a
security boundary. Use `plan` or an external sandbox when those actions must be
technically prevented.

The packet scanner refuses several common secret formats, but it cannot protect
secrets stored in files once workspace tools are enabled. Material Claude reads
may be sent to Anthropic. Review [SECURITY.md](SECURITY.md) before enabling
workspace access.

## Requirements

- Node.js 22 or newer
- npm 10 or newer
- Claude Code 2.1.207 or newer available on `PATH`
- Claude Code authentication compatible with the configured auth policy
- An MCP client such as Codex

## Install

Install the command from npm:

```bash
npm install --global claude-second-opinion-mcp
```

To install from source instead:

```bash
git clone https://github.com/Borge-Labs/claude-second-opinion-mcp.git
cd claude-second-opinion-mcp
npm ci
npm run build
npm test
npm run smoke
```

`dist/` is generated locally and intentionally not committed.

## Codex configuration

Add a server entry to `~/.codex/config.toml`, replacing the workspace root with
an absolute path on your machine:

```toml
[mcp_servers.claude_second_opinion]
command = "claude-second-opinion-mcp"
enabled = true
enabled_tools = ["start_claude_review", "get_claude_review", "cancel_claude_review"]
startup_timeout_sec = 20
tool_timeout_sec = 75

[mcp_servers.claude_second_opinion.env]
CLAUDE_SECOND_OPINION_CLAUDE_BIN = "claude"
CLAUDE_SECOND_OPINION_MODEL = "best"
CLAUDE_SECOND_OPINION_FALLBACK_MODEL = "opus"
CLAUDE_SECOND_OPINION_AUTH_POLICY = "max"
CLAUDE_SECOND_OPINION_ENV_POLICY = "clean"
CLAUDE_SECOND_OPINION_WORKSPACE_ROOTS_JSON = '["/absolute/path/to/projects"]'
CLAUDE_SECOND_OPINION_PERMISSION_MODE = "plan"
CLAUDE_SECOND_OPINION_SAFE_MODE = "true"
```

If you prefer not to install globally, use `command = "npx"` and
`args = ["--yes", "claude-second-opinion-mcp@latest"]`. A source checkout can
still use `command = "node"`, its built `dist/index.js`, and the checkout as
`cwd`.

Restart Codex after adding the server or rebuilding it so the MCP process and
tool inventory are refreshed.

### Explicit full-agent configuration

If the Claude process already runs inside a trusted external sandbox and you
want project customizations plus unrestricted tool authorization:

```toml
[mcp_servers.claude_second_opinion.env]
CLAUDE_SECOND_OPINION_WORKSPACE_ROOTS_JSON = '["/absolute/path/to/projects"]'
CLAUDE_SECOND_OPINION_PERMISSION_MODE = "bypassPermissions"
CLAUDE_SECOND_OPINION_SAFE_MODE = "false"
```

The value `--dangerously-skip-permissions` is accepted as an alias and is
normalized internally to `bypassPermissions`. Environment values are parsed as
configuration, never appended as raw command-line flags.

## Calling the tools

Start a workspace-aware review:

```json
{
  "kind": "final_check",
  "task": "Review the current implementation before release.",
  "workspace": "/absolute/path/to/projects/example",
  "artifact": "Focus on the current git diff and the stated release criteria.",
  "question": "Is any material correctness, security, or compatibility risk still unsupported by evidence?",
  "focus": ["correctness", "security", "test evidence"]
}
```

The start result includes a `review_id` and execution profile. Continue useful
work, then call `get_claude_review` with that ID. Omit the ID to retrieve the
most recently started review in the current MCP process.

Completed reviews include `permission_denials`. The bridge exposes only denied
tool names, not tool inputs, and changes the verdict to `insufficient_context`
whenever that list is non-empty.

MCP cannot reliably inject a completed background result into an already
running model turn, so the caller must explicitly retrieve it later.

## Suggested agent guidance

This belongs in user or project agent instructions, not in a hard-coded hook:

```markdown
Use the optional Claude second-opinion MCP selectively when another model
perspective is likely to affect a consequential decision. Start a useful review
early, continue independent work while it runs, and retrieve and reconcile the
result before a relevant final handoff. Do not use it for routine or mechanical
work or repeat a review on materially unchanged input.
```

## Configuration reference

| Variable | Default | Purpose |
|---|---:|---|
| `CLAUDE_SECOND_OPINION_CLAUDE_BIN` | `claude` | Claude Code executable |
| `CLAUDE_SECOND_OPINION_MODEL` | `best` | Primary model or selector |
| `CLAUDE_SECOND_OPINION_FALLBACK_MODEL` | `opus` | Native and zero-work capacity fallback model |
| `CLAUDE_SECOND_OPINION_AUTH_POLICY` | `max` | `max`, `first_party`, or `any` |
| `CLAUDE_SECOND_OPINION_ENV_POLICY` | `clean` | Reviewed operational/network allowlist or explicit `inherit` |
| `CLAUDE_SECOND_OPINION_WORKSPACE_ROOTS_JSON` | `[]` | JSON array of trusted absolute workspace roots |
| `CLAUDE_SECOND_OPINION_PERMISSION_MODE` | `plan` | Valid Claude Code permission mode or dangerous alias |
| `CLAUDE_SECOND_OPINION_SAFE_MODE` | `true` | Disable Claude customizations for workspace reviews |
| `CLAUDE_SECOND_OPINION_ALLOWED_TOOLS_JSON` | `[]` | JSON array passed as Claude allowed-tool rules |
| `CLAUDE_SECOND_OPINION_DISALLOWED_TOOLS_JSON` | `[]` | JSON array passed as Claude denied-tool rules |
| `CLAUDE_SECOND_OPINION_TIMEOUT_MS` | `5400000` | Review timeout, 90 minutes |
| `CLAUDE_SECOND_OPINION_MAX_INPUT_BYTES` | `140000` | Maximum serialized review packet size |
| `CLAUDE_SECOND_OPINION_MAX_BUDGET_USD` | `2` | Claude print-mode budget ceiling when applicable |
| `CLAUDE_SECOND_OPINION_DEBUG` | unset | Set to `1` for bounded Claude diagnostics on failures |

`--effort max` is always passed, and the child environment is forced to
`CLAUDE_CODE_EFFORT_LEVEL=max` so an inherited lower setting cannot override
it.

Auth policies:

- `max`: require first-party Claude.ai Max and remove provider overrides.
- `first_party`: require first-party Claude.ai authentication and remove
  provider overrides.
- `any`: accept any logged-in Claude Code provider configuration. Combine this
  with `ENV_POLICY=inherit` only when intentionally using credentials supplied
  through environment variables.

The clean environment is a reviewed operational allowlist, not a secret-free
boundary. It retains standard proxy, certificate, mTLS path, and
`CLAUDE_CONFIG_DIR` settings needed for supported Claude installations. Proxy
URLs can contain credentials. The mTLS key passphrase is deliberately omitted;
set `ENV_POLICY=inherit` only when that credential is required. Use an external
sandbox when workspace tools must not be able to observe process configuration.

Tool allow rules pre-authorize matching tools; they are not a filesystem
sandbox. Deny rules and Claude Code sandbox settings provide additional layers.

## Job lifecycle

- Only one review runs at a time.
- The default internal timeout is 90 minutes.
- Start/get/cancel MCP calls remain short.
- Job state is retained in memory for the active MCP process.
- Restarting or disconnecting the MCP client cancels active work and clears
  retained results.
- Timeout, cancellation, and shutdown send TERM and then KILL to the Claude
  process group on supported platforms.

## Development

```bash
npm ci
npm test
npm run smoke
npm audit --omit=dev
```

The unit suite uses a fake Claude executable and makes no model calls. The
optional live test consumes Claude usage:

```bash
npm run smoke:live
```

To exercise real workspace inspection, point the smoke test at a trusted root
that is also covered by the configured workspace roots. The script creates and
removes a temporary sentinel fixture, proves Claude read it, and verifies that
the fixture was not modified. The live harness forces plan permission mode and
safe mode for this check:

```bash
CLAUDE_SECOND_OPINION_WORKSPACE_ROOTS_JSON='["/absolute/path/to/projects"]' \
CLAUDE_SECOND_OPINION_SMOKE_WORKSPACE=/absolute/path/to/projects \
npm run smoke:live
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidance and
[CHANGELOG.md](CHANGELOG.md) for release history.

## Project and trademark notice

The public GitHub repository is a mirror of the canonical Gitea repository.
GitHub issues and pull requests are welcome and are integrated upstream before
being mirrored back.

This is an independent open-source project. It is not affiliated with,
endorsed by, or sponsored by Anthropic or OpenAI. Claude, Claude Code, Codex,
and related names are trademarks of their respective owners.

## How this was built

Most of this codebase was written by AI agents - deliberately. Borge Labs runs
a supervised multi-agent delivery loop: frontier models plan, review and take
the hard judgment calls, while cheaper API models and local open-weight models
running on my own GPUs (Qwen-class models via llama.cpp and vLLM on a
bare-metal Kubernetes fleet) implement the lighter tiers. Changes pass
cross-model review - a different model than the one that wrote them - and land
only through human-owned specification and merge gates. The public history
starts at a squashed release commit because the private history predates the
identity and privacy purge; ongoing commits carry co-author trailers. The full
operating model, including its honest limitations, is written up in the
[supervised delivery loop note](https://borge-labs.no/notes/supervised-multi-agent-delivery/).

## License

MIT License. See [LICENSE](LICENSE).