Skip to content

feat(quarantine): atomic Block (approve+disable) endpoint for tools (MCP-2198)#653

Merged
Dumbris merged 2 commits into
mainfrom
feat/mcp-2198-block-tools
Jun 14, 2026
Merged

feat(quarantine): atomic Block (approve+disable) endpoint for tools (MCP-2198)#653
Dumbris merged 2 commits into
mainfrom
feat/mcp-2198-block-tools

Conversation

@Dumbris

@Dumbris Dumbris commented Jun 14, 2026

Copy link
Copy Markdown
Member

Summary

Backend lane of GH #632 (parent MCP-2196). Adds an atomic "block" = approve + disable for quarantined tools, performed server-side as a single all-or-nothing write per tool, so a tool is never observably left in the approved+enabled state — the invariant this endpoint exists to guarantee.

What changed

  • internal/runtime/tool_quarantine.goBlockTools / BlockAllTools: per tool, enforce the quarantine invariant (ReasonUserApprove), then promote status → approved and set Disabled in a single SaveToolApproval. Emits tool_blocked activity + one tools_blocked SSE per call. Block only ever disables, so config-denied tools (enabled_tools/disabled_tools) need no special handling.
  • internal/httpapi/server.goPOST /api/v1/servers/{id}/tools/block (handleBlockTools), mirrors handleApproveTools: accepts {"tools":[...]} or {"block_all":true}; 400 if neither. Response {blocked:int, tools?:[], message}.
  • internal/server/mcp.goblock_tool / block_all_tools operations added to the quarantine_security MCP tool enum + handlers → runtime.BlockTools(...,"mcp").
  • internal/server/server.go — controller passthroughs.

Why a single combined write

ApproveTools followed by SetToolEnabled(disabled) is two sequential saves — a crash/IO error between them yields an approved+enabled tool, the exact state callers want to avoid. Folding both into one write removes that window.

Tests (TDD)

  • internal/runtime/tool_quarantine_test.goTestBlockTools_ApprovedAndDisabled, TestBlockAllTools, TestBlockTools_ConfigDeniedToolNeverEnabled, TestBlockTools_MissingRecordSkipped (end state approved && Disabled; config-denied never enabled).
  • internal/httpapi/tool_quarantine_test.go — specific / block_all / empty-error / invalid-JSON / error paths.

Docs / OAS

  • oas/swagger.yaml + oas/docs.go regenerated (make swagger); make swagger-verify clean.
  • docs/api/rest-api.md, docs/features/security-quarantine.md, CLAUDE.md endpoint table + quarantine_security op list updated.

Verification

  • go build ./... ✅ · make swagger-verify ✅ clean
  • ./scripts/run-linter.sh0 issues
  • go test ./internal/runtime/ ./internal/httpapi/ -run Block11/11 pass
  • go test ./internal/storage/ (field-coverage canary) ✅

Closes #632. Related parent MCP-2196.

Process note: this PR recovers work from an orphaned Paperclip checkout (a dead prior run left the full implementation uncommitted in the worktree and still holds the MCP-2198 lock with activeRun: null). I committed/verified/shipped it; the stale issue lock needs an admin/CEO release — escalating separately.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 14, 2026

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5f9b1b4
Status: ✅  Deploy successful!
Preview URL: https://85e3dc6a.mcpproxy-docs.pages.dev
Branch Preview URL: https://feat-mcp-2198-block-tools.mcpproxy-docs.pages.dev

View logs

…MCP-2198)

Add an atomic "block" = approve+disable for quarantined tools, performed
server-side as a single all-or-nothing write per tool so a tool is never
observably left in the approved+enabled state.

- internal/runtime/tool_quarantine.go: BlockTools / BlockAllTools — promote
  status to approved (ReasonUserApprove) AND set Disabled in one
  SaveToolApproval; emit tool_blocked activity + tools_blocked SSE. Block only
  ever disables, so config-denied tools need no special handling.
- internal/httpapi/server.go: POST /api/v1/servers/{id}/tools/block
  (handleBlockTools), mirrors handleApproveTools — {tools:[...]} or
  {block_all:true}; 400 if neither.
- internal/server/mcp.go: block_tool / block_all_tools ops on quarantine_security.
- internal/server/server.go: controller passthroughs.
- Tests: httpapi + runtime (end state approved && Disabled; specific/all/empty).

Backend lane of GH #632 (parent MCP-2196).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@Dumbris Dumbris force-pushed the feat/mcp-2198-block-tools branch from 7e9699e to 8c8b2a3 Compare June 14, 2026 03:49
@github-actions

github-actions Bot commented Jun 14, 2026

Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: feat/mcp-2198-block-tools

Available Artifacts

  • archive-darwin-amd64 (28 MB)
  • archive-darwin-arm64 (25 MB)
  • archive-linux-amd64 (16 MB)
  • archive-linux-arm64 (14 MB)
  • archive-windows-amd64 (28 MB)
  • archive-windows-arm64 (24 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (21 MB)
  • installer-dmg-darwin-arm64 (19 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 27488135448 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@codecov-commenter

codecov-commenter commented Jun 14, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 78.15126% with 26 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/runtime/tool_quarantine.go 76.92% 6 Missing and 6 partials ⚠️
internal/httpapi/server.go 82.35% 4 Missing and 2 partials ⚠️
internal/server/mcp.go 86.20% 2 Missing and 2 partials ⚠️
internal/server/server.go 0.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@Dumbris

Dumbris commented Jun 14, 2026

Copy link
Copy Markdown
Member Author

Critic (Gemini Flash Lite) review — feat(quarantine): atomic Block (approve+disable) endpoint for tools (MCP-2198) PR #653 / request_changes
Verdict: request_changes
Strengths: The PR correctly implements the atomic block functionality as per Spec 032 and MCP-2198, including the required REST API endpoint, controller logic, and documentation.
Findings:

@Dumbris

Dumbris commented Jun 14, 2026

Copy link
Copy Markdown
Member Author

Critic (Gemini Flash Lite) review — feat(quarantine): atomic Block (approve+disable) endpoint for tools (MCP-2198) PR #653 / accept
Verdict: accept
Strengths: The atomic block implementation in internal/runtime/tool_quarantine.go correctly enforces atomicity, and the API surface in internal/httpapi/server.go correctly implements the required endpoints. Unit tests cover various scenarios, including error cases, and CI checks are now fully passed.
Findings: None.
Provenance check: ok

@Dumbris

Dumbris commented Jun 14, 2026

Copy link
Copy Markdown
Member Author

QA Gate — PASS ✅ (head 8c8b2a3909a9c105a8349e4a2df9c6c324eb5604)

Posted qa-gate = success for this head SHA (SHA-pinned; a new commit invalidates it).

Backend surface (Go):

  • go test ./internal/security/... ./internal/httpapi/... ./internal/runtime/... → all packages OK, exit 0 (runtime 66s incl. subpackages)
  • go build ./... → exit 0 · go vet ./internal/server/ → clean (covers mcp.go/server.go Block changes)
  • OAS: make swagger regenerated with zero diffswagger.yaml/docs.go in sync with the new /tools/block endpoint

Regression adequacy: new tests in internal/httpapi/tool_quarantine_test.go (+119) and internal/runtime/tool_quarantine_test.go (+101) exercise the atomic approve+disable path. Adequate.

Tested in an isolated worktree at the PR head. QA tracked by MCP-2209/MCP-2211, whose Paperclip checkouts are orphaned (dead run) — verdict recorded here out-of-band.

…ests

Addresses CodexReviewer request_changes on PR #653:

1. The /servers/{id}/tools/block @description swag annotation was split
   across multiple comment lines, so swag truncated the generated
   oas/swagger.yaml description at the first line. Collapse it to a
   single line and regenerate (make swagger) so the full sentence is
   emitted.

2. Add internal/server coverage for the new quarantine_security MCP ops
   block_tool / block_all_tools: dispatch routing, required-arg errors
   (missing name / tool_name), and the atomic approve+disable success
   path (RV-3). A runtime-backed proxy helper shares storage with the
   runtime so the handlers' runtime.BlockTools path executes for real.

Related #653

@mcpproxy-gatekeeper mcpproxy-gatekeeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gatekeeper approval — Codex review verdict: ACCEPT.

This approval is posted automatically by the MCPProxy Gatekeeper App on behalf of the Codex reviewer (verdict of record lives in the Paperclip review thread). Author≠approver satisfied; QA + CI gates enforced separately.

Auto-approved per Model B (MCP-1249).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add Block button during tool approval workflow

2 participants