Repository navigation
Make MCP compile read-only unless write tools are enabled - #69510
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅
|
|
✅ Ponytail Reviewer completed successfully! Ponytail ReviewerOutcome: The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible. Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.
|
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft. Commit pushed:
|
|
✅ Test Quality Sentinel completed test quality analysis. Test Quality SentinelOutcome: The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible. Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.
|
|
✅ PR Code Quality Reviewer completed the code quality review.
|
🏗️ Design Decision Gate — ADR RequiredThis PR makes significant changes to core business logic (303 new lines in 📄 Draft ADR committed: The draft records this decision: MCP
📋 What to do next
Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision. ❓ Why ADRs Matter
ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you. 📋 Michael Nygard ADR Format ReferenceAn ADR must contain these four sections to be considered complete:
All ADRs are stored in
|
Comment MemoryPeek at saved memory (pr-code-quality-reviewer)Note This comment is managed by comment memory.Expand the saved memory block to view or edit the persistent context for this thread.
|
There was a problem hiding this comment.
Request changes
Read-only MCP compile still has a cache-root correctness hole: cached remote imports can fail as uncached when the server is launched from a checkout subdirectory.
Blocking theme
- The new shared import-cache path is tied to the process working directory, not the repository root, so the advertised "cached remote imports work in --no-emit" behavior is not stable outside repo-root launches.
Static review only; I did not run code or tests.
🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 97.6 AIC · ⌖ 5.76 AIC · ⊞ 12.7K
Comment /review to run again
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /diagnosing-bugs and /codebase-design to the MCP compile access-control changes. Overall this is a well-scoped, well-tested change — 2 non-blocking improvement suggestions posted inline.
📋 Key Themes & Highlights
Key Themes
- Minor perf/UX gap: read-only import resolution still does a network SHA lookup before rejecting uncached imports (pkg/parser/remote_workflow_spec.go).
- Duplication risk: the read-only write-flag gate list is repeated in two places in mcp_tools_readonly.go; worth consolidating into one helper to avoid drift as new scanners are added.
Positive Highlights
- ✅ Excellent regression coverage: TestMCPServer_ReadOnlyCompilePreservesFiles exercises existing/new files x all/single-workflow x valid/invalid/oversized/cached/uncached-import combinations, snapshotting file mode/mtime/hash to prove true read-only behavior.
- ✅ ImportCache.SetReadOnly and the explicit "not cached" error give MCP clients an actionable message instead of a confusing permission or network failure.
- ✅ Compiler.writeInvalidWorkflowYAML and runPostProcessing/runPostProcessingForDirectory correctly gate all diagnostic/side-effect writes behind NoEmit, consistent with the PR's stated goal.
- ✅ Middleware change cleanly separates compile (now allowed by default, actor-checked only when write-enabled) from add/update/fix (still fully gated), matching the documented access-control model.
No blocking issues found; approving pending author's discretion on the two inline suggestions.
@copilot please address the review comments above.
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 134.3 AIC · ⌖ 15.1 AIC · ⊞ 10.1K
Comment /matt to run again
Comments that could not be inline-anchored
pkg/parser/remote_workflow_spec.go:29
[/diagnosing-bugs] In read-only mode, resolveWorkflowSpecSHAForCache still performs a full network SHA resolution (REST API call / git ls-remote fallback) for every uncached import before the cache.readOnly check rejects it. This defeats the goal of making read-only compile fast/offline-safe and can surface confusing network/auth errors instead of the clean not cached message when the MCP client has no network credentials.
<details>
<summary>💡 Suggested fix</summary>
Check `cache…
pkg/cli/mcp_tools_readonly.go:146
[/codebase-design] The read-only rejection list (Fix || DryRun || Zizmor || Poutine || ...) duplicates the scanner-flag list used again at line 179 (!args.DryRun && (Zizmor || Poutine || ...)). A future scanner flag added to compileArgs only needs to appear in one of these lists to silently bypass the read-only gate.
<details>
<summary>💡 Suggested fix</summary>
Extract a single helper method on compileArgs, e.g. requiresWriteTools() bool returning the OR of all write-requiring flags, and…
There was a problem hiding this comment.
🟡 Changes recommended
The read-only contract and existing validation paths have unresolved correctness issues.
5 open issues
Update MCP inspect test for read-only compile support · New No-emit callers fail on uncached imports before cache refresh · New Stale malformed lock files invalidate valid no-emit compilations · New Read-only compilation can pull missing Docker images · New Remote includes bypass read-only cache and create temporary files · New
What changed in this PR
Enables MCP workflow validation without write-tool permission and tightens the compiler’s no-emit behavior.
Changes:
- Adds capability-aware compile defaults and option restrictions.
- Suppresses output writes and introduces read-only import caches.
- Updates regression tests and MCP documentation.
| File | Description |
|---|---|
| pkg/workflow/compiler.go | Suppresses diagnostic YAML writes in no-emit mode. |
| pkg/workflow/compiler_yaml.go | Reuses the shared cache for hashing. |
| pkg/workflow/compiler_template_injection_both_paths_test.go | Tests diagnostic-file preservation. |
| pkg/workflow/compiler_mutators.go | Applies read-only import-cache mode. |
| pkg/parser/remote_workflow_spec.go | Rejects uncached read-only imports. |
| pkg/parser/import_cache.go | Adds read-only cache controls. |
| pkg/parser/import_cache_test.go | Tests cache write restrictions. |
| pkg/cli/mcp_write_tools.go | Allows default compile calls. |
| pkg/cli/mcp_write_tools_test.go | Tests capabilities and argument restrictions. |
| pkg/cli/mcp_tools_readonly.go | Configures read-only compilation defaults. |
| pkg/cli/mcp_tools_output_streams_test.go | Updates write-enabled test registration. |
| pkg/cli/mcp_tool_arguments_test.go | Updates argument-test setup. |
| pkg/cli/mcp_server.go | Propagates write capability to compilation. |
| pkg/cli/mcp_server_unit_test.go | Updates compile-tool registration. |
| pkg/cli/mcp_server_compile_test.go | Tests file preservation and enabled emission. |
| pkg/cli/mcp_server_command.go | Documents compile access controls. |
| pkg/cli/mcp_positional_args_test.go | Updates positional-argument test setup. |
| pkg/cli/mcp_compile_diagnostics_test.go | Preserves write-enabled diagnostic tests. |
| pkg/cli/mcp_actionlint_local_test.go | Updates scanner-test registration. |
| pkg/cli/compile_pipeline.go | Skips no-emit post-processing. |
| docs/src/content/docs/setup/cli.md | Explains default read-only compilation. |
| docs/src/content/docs/reference/tools.md | Documents write-capability requirements. |
| docs/src/content/docs/reference/gh-aw-as-mcp-server.md | Describes no-emit and cached-import behavior. |
Review effort: Max
Document MCP-COMPILE-001 through MCP-COMPILE-006 and add a bounded host-side TLA+ model with negative controls and reachability witnesses. Refresh the replaced official TLC release asset pin and regenerate its workflow. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Separate the MCP read-only boundary from trusted CLI no-emit imports, propagate the shared cache through nested include expansion, ignore stale output during no-emit validation, and prevent Docker pulls. Update configuration-only inspection coverage, documentation, and bounded formal controls. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
🎉 This pull request is included in a new release. Release: |


Summary
MCP clients should be able to validate workflows without permission to modify them. Previously,
compilerequired--allow-write-tools, and--no-emitstill allowed filesystem side effects.compileavailable by default with a dedicated internal read-only mode, automatic--no-emit --no-check-update, and a read-only tool annotation. Preserve configured actor checks for write-enabled compilation and the startup capability gate for writing tools.validate, maintenance jobs, and label discovery, including changed upstream branch SHAs..gitattributespost-processing, cache writes, and temporary remote-include downloads in read-only mode. Validate newly generated YAML rather than malformed stale lock files.--allow-write-tools, with shared option-gate helpers.ADR: ADR-69510: Read-Only MCP compile by Default (Draft)
Validation
All five requested inline review comments were accepted, addressed, replied to, and resolved in the final review-fix batch.