Remote-MCP 07: Split local-filesystem tools from remotable tools and fail closed on the boundary #936

Open
opened 2026-07-26 00:32:41 -05:00 by jcwalker3 · 0 comments
Owner

Parent epic: #929.
Depends on: #930.

Problem

A large share of the tool surface acts on the operator's disk, not on Gitea. Worktree bootstrap and binding, issue-lock worktree checks, dirty-checkout assessment, conflict-fix classification, branch publication from a local checkout, and the startup scan that walks the install directory all assume the files in question are reachable from the server process.

Those tools cannot be served from a remote host. The danger is not that they fail; it is that they might appear to succeed against the host's own filesystem, reporting a clean worktree or a successful binding that has nothing to do with the caller's machine. A gate that inspects the wrong disk and returns green is worse than one that refuses. The tool surface therefore needs an explicit, enforced boundary before any endpoint exists.

Scope

Classify every registered tool and enforce the classification.

  • Assign each tool exactly one class: fully remotable, requires the caller's local filesystem, or requires a server-side canonical checkout.
  • Record the classification next to the tool definition so it cannot drift silently from the implementation.
  • Enforce at dispatch: a tool requiring the caller's local filesystem, invoked over a transport that cannot reach it, fails closed with a named blocker before performing any work.
  • Make the refusal state which class the tool is in and what the caller must do instead, rather than returning a generic denial.
  • Ensure a refused call performs no mutation, leaves no partial state, and keeps the transport alive.
  • Add a test that fails when a newly registered tool carries no classification, so the boundary cannot rot.

Acceptance criteria

  • Every registered tool has exactly one recorded class, with no unclassified tools remaining.
  • A test enumerates registered tools and fails on any missing or duplicate classification.
  • Under stdio, all classes remain callable and behavior is unchanged.
  • Over a transport that cannot reach the caller's filesystem, a local-filesystem tool is refused before doing any work, with a named blocker and a stated alternative.
  • A refused call performs no mutation and leaves no partial state.
  • The classification is discoverable by an operator without reading the source.

Verification

  • Tests cover: each class under stdio, each class under a non-local transport, the unclassified-tool guard, and the no-partial-state assertion.
  • A test asserts that a refused local-filesystem tool never touched the host filesystem.
  • Full suite compared against the recorded baseline, run from a branches/ worktree.

Non-goals

  • Making local-filesystem tools work remotely. Some genuinely cannot, and this child records that rather than solving it.
  • Removing any tool.
  • Designing a client-side filesystem bridge.
Parent epic: #929. Depends on: #930. ## Problem A large share of the tool surface acts on the operator's disk, not on Gitea. Worktree bootstrap and binding, issue-lock worktree checks, dirty-checkout assessment, conflict-fix classification, branch publication from a local checkout, and the startup scan that walks the install directory all assume the files in question are reachable from the server process. Those tools cannot be served from a remote host. The danger is not that they fail; it is that they might appear to succeed against the host's own filesystem, reporting a clean worktree or a successful binding that has nothing to do with the caller's machine. A gate that inspects the wrong disk and returns green is worse than one that refuses. The tool surface therefore needs an explicit, enforced boundary before any endpoint exists. ## Scope Classify every registered tool and enforce the classification. - Assign each tool exactly one class: fully remotable, requires the caller's local filesystem, or requires a server-side canonical checkout. - Record the classification next to the tool definition so it cannot drift silently from the implementation. - Enforce at dispatch: a tool requiring the caller's local filesystem, invoked over a transport that cannot reach it, fails closed with a named blocker before performing any work. - Make the refusal state which class the tool is in and what the caller must do instead, rather than returning a generic denial. - Ensure a refused call performs no mutation, leaves no partial state, and keeps the transport alive. - Add a test that fails when a newly registered tool carries no classification, so the boundary cannot rot. ## Acceptance criteria - Every registered tool has exactly one recorded class, with no unclassified tools remaining. - A test enumerates registered tools and fails on any missing or duplicate classification. - Under stdio, all classes remain callable and behavior is unchanged. - Over a transport that cannot reach the caller's filesystem, a local-filesystem tool is refused before doing any work, with a named blocker and a stated alternative. - A refused call performs no mutation and leaves no partial state. - The classification is discoverable by an operator without reading the source. ## Verification - Tests cover: each class under stdio, each class under a non-local transport, the unclassified-tool guard, and the no-partial-state assertion. - A test asserts that a refused local-filesystem tool never touched the host filesystem. - Full suite compared against the recorded baseline, run from a `branches/` worktree. ## Non-goals - Making local-filesystem tools work remotely. Some genuinely cannot, and this child records that rather than solving it. - Removing any tool. - Designing a client-side filesystem bridge.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Scaled-Tech-Consulting/Gitea-Tools#936