Add Option-C tool-isolation knobs: strict MCP config, mcp-config path, setting sources, DontAsk permission mode #46

Open
opened 2026-04-21 14:43:26 +00:00 by mprihoda · 0 comments
mprihoda commented 2026-04-21 14:43:26 +00:00 (Migrated from github.com)

Context

Consumer: iterative-works/procedures, issue PROC-401 (ADR 0001 Step 2 — split event processing into isolated Claude CLI sessions). We need to spawn event-processor subprocesses with a hard tool allow-list that cannot be widened by parent .mcp.json files, user-level settings, or permission prompts.

Per the Claude Code CLI docs, the full "Option C" isolation requires four CLI flags that claude-code-query does not currently expose through QueryOptions / SessionOptions / PermissionMode / CLIArgumentBuilder:

CLI flag Purpose
--strict-mcp-config Prevent merging of any .mcp.json other than the one we pass.
--mcp-config <path> Explicitly point at a specific .mcp.json (don't rely on cwd lookup).
--setting-sources <csv> Restrict settings resolution (e.g., project — excludes user/managed).
--permission-mode dontAsk Enforce allowedTools as a hard allow-list (no prompts, no hangs).

Without all four, an isolated subprocess can either (a) inherit the user's .mcp.json / ~/.claude/settings.json, or (b) hang on a permission prompt for a tool not in allowedTools. Neither is acceptable for the event-processor use case.

Proposed additions

1. QueryOptions (and symmetrically, SessionOptions)

Add three fields with matching fluent with* helpers:

  • strictMcpConfig: Boolean = false + withStrictMcpConfig(flag: Boolean)
  • mcpConfigPath: Option[String] = None + withMcpConfigPath(path: String)
  • settingSources: List[String] = Nil + withSettingSources(sources: List[String])

Defaults preserve current behaviour (no flag emitted).

2. PermissionMode enum

Add one case alongside the existing Default / AcceptEdits / BypassPermissions:

enum PermissionMode:
  case Default
  case AcceptEdits
  case BypassPermissions
  case DontAsk   // ← new

3. CLIArgumentBuilder

Both match sites (around lines 50 and 113 today — the query and session arg builders) need:

  • A new branch for PermissionMode.DontAsk emitting List("--permission-mode", "dontAsk").
  • Translation of strictMcpConfig == trueList("--strict-mcp-config").
  • Translation of mcpConfigPath.foreachList("--mcp-config", path).
  • Translation of settingSources non-empty → List("--setting-sources", sources.mkString(",")).

Empty/false defaults must NOT emit any flag (preserves backwards compatibility).

Tests

  • CLIArgumentBuilderTest — one case per new field (emitted when set, absent when default) plus the new PermissionMode branch.
  • SessionOptionsArgsTest / QueryOptionsArgsTest — symmetric coverage.
  • Smoke: QueryOptions().withStrictMcpConfig(true).withMcpConfigPath("./.mcp.json").withSettingSources(List("project")).withPermissionMode(PermissionMode.DontAsk) round-trips to argv containing all four flags in deterministic order.

Non-goals

  • No behavioural changes to existing flags.
  • No new PermissionMode aliasing or deprecation.
  • No changes to streaming / log parsing surfaces.

Delivery

  • Target version: publish under continuing 0.3.0-SNAPSHOT on a feature branch; consumer (PROC-401) bumps its Maven coord to the snapshot and validates.
  • A proper v0.3.0 tag is cut later, after the additions are exercised in real PROC-401 use and any follow-up fixes settle.

Upstream reference

  • ADR 0001 Step 2 in the procedures repo (private): drives the requirement.
  • Claude Code CLI docs: rationale for why dontAsk + --strict-mcp-config + --setting-sources project is the minimum set for non-interactive isolation.
## Context Consumer: `iterative-works/procedures`, issue PROC-401 (ADR 0001 Step 2 — split event processing into isolated Claude CLI sessions). We need to spawn event-processor subprocesses with a **hard** tool allow-list that cannot be widened by parent `.mcp.json` files, user-level settings, or permission prompts. Per the Claude Code CLI docs, the full "Option C" isolation requires four CLI flags that `claude-code-query` does not currently expose through `QueryOptions` / `SessionOptions` / `PermissionMode` / `CLIArgumentBuilder`: | CLI flag | Purpose | | --------------------------------- | ---------------------------------------------------------------------- | | `--strict-mcp-config` | Prevent merging of any `.mcp.json` other than the one we pass. | | `--mcp-config <path>` | Explicitly point at a specific `.mcp.json` (don't rely on cwd lookup). | | `--setting-sources <csv>` | Restrict settings resolution (e.g., `project` — excludes user/managed).| | `--permission-mode dontAsk` | Enforce `allowedTools` as a hard allow-list (no prompts, no hangs). | Without all four, an isolated subprocess can either (a) inherit the user's `.mcp.json` / `~/.claude/settings.json`, or (b) hang on a permission prompt for a tool not in `allowedTools`. Neither is acceptable for the event-processor use case. ## Proposed additions ### 1. `QueryOptions` (and symmetrically, `SessionOptions`) Add three fields with matching fluent `with*` helpers: - `strictMcpConfig: Boolean = false` + `withStrictMcpConfig(flag: Boolean)` - `mcpConfigPath: Option[String] = None` + `withMcpConfigPath(path: String)` - `settingSources: List[String] = Nil` + `withSettingSources(sources: List[String])` Defaults preserve current behaviour (no flag emitted). ### 2. `PermissionMode` enum Add one case alongside the existing `Default / AcceptEdits / BypassPermissions`: ```scala enum PermissionMode: case Default case AcceptEdits case BypassPermissions case DontAsk // ← new ``` ### 3. `CLIArgumentBuilder` Both match sites (around lines 50 and 113 today — the `query` and `session` arg builders) need: - A new branch for `PermissionMode.DontAsk` emitting `List("--permission-mode", "dontAsk")`. - Translation of `strictMcpConfig == true` → `List("--strict-mcp-config")`. - Translation of `mcpConfigPath.foreach` → `List("--mcp-config", path)`. - Translation of `settingSources` non-empty → `List("--setting-sources", sources.mkString(","))`. Empty/false defaults must NOT emit any flag (preserves backwards compatibility). ## Tests - `CLIArgumentBuilderTest` — one case per new field (emitted when set, absent when default) plus the new `PermissionMode` branch. - `SessionOptionsArgsTest` / `QueryOptionsArgsTest` — symmetric coverage. - Smoke: `QueryOptions().withStrictMcpConfig(true).withMcpConfigPath("./.mcp.json").withSettingSources(List("project")).withPermissionMode(PermissionMode.DontAsk)` round-trips to argv containing all four flags in deterministic order. ## Non-goals - No behavioural changes to existing flags. - No new `PermissionMode` aliasing or deprecation. - No changes to streaming / log parsing surfaces. ## Delivery - Target version: publish under continuing `0.3.0-SNAPSHOT` on a feature branch; consumer (PROC-401) bumps its Maven coord to the snapshot and validates. - A proper `v0.3.0` tag is cut **later**, after the additions are exercised in real PROC-401 use and any follow-up fixes settle. ## Upstream reference - ADR 0001 Step 2 in the procedures repo (private): drives the requirement. - Claude Code CLI docs: rationale for why `dontAsk` + `--strict-mcp-config` + `--setting-sources project` is the minimum set for non-interactive isolation.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
iterative-works/claude-code-query#46
No description provided.