feat(mcp): expose branch-backed workspace discovery#10566

merged1
agents/mcp-thin/project-launches
agents/mcp-thin/workspace-refs
openedSep 7, 2026byJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 3d ago
2 unresolved threads · Jump to file
Selected thread: apps/server/src/mcp/toolkits/worktree/handlers.ts:33 — This handler carries real backend behavior (capability gate, project lookup / not-found path, and the `caller.worktreePath ?? project.workspaceRoot` cwd choice) but no focused test covers it. The sibling worktree tools keep this logic in `WorktreeMcpService`, which is tested in `WorktreeMcpService.test.ts` with `Layer.mock` test layers for `ThreadManagementService` / `ProjectService` / `GitWorkflowService`. Consider either moving the resolution logic into `WorktreeMcpService` (e.g. a `listRefs(scope, input)` operation) and covering it in that existing harness, or adding a focused handler test that provides test layers for `ProjectService` and `GitWorkflowService` and asserts the capability denial, project-not-found, and worktree-vs-workspace-root cwd cases. <sup>Posted via Macroscope — <a href="https://github.com/pingdotgg/t3code/runs/101848249634">Effect Service Conventions</a></sup> <!-- macroscope-meta: {"kind":"check_run","config":".macroscope/check-run-agents/effect-service-conventions.md","check":"Effect Service Conventions"} -->
0 / 5 viewed5 files changedLine totals unavailable

apps/server/src/mcp/toolkits/worktree/handlers.ts · Earlier revision 18c4af6a · right 13–33

Original diff
@@ -1,10 +1,36 @@
+import { OrchestratorMcpFailure } from "@t3tools/contracts";
+import * as Option from "effect/Option";
+import * as GitWorkflow from "../../../git/GitWorkflowService.ts";
+import * as Project from "../../../project/ProjectService.ts";
+import { readCaller, unavailable } from "../../threadAccess.ts";
 import * as Effect from "effect/Effect";
 
 import { McpInvocationContext } from "../../McpInvocationContext.ts";
 import { WorktreeMcpService } from "../../WorktreeMcpService.ts";
 import { WorktreeToolkit } from "./tools.ts";
 
 const handlers = {
+  t3_worktree_list: (input) =>
+    Effect.gen(function* () {
+      const context = yield* McpInvocationContext;
+      if (!context.capabilities.has("worktree"))
+        return yield* new OrchestratorMcpFailure({
+          code: "capability_denied",
+          message: "This credential cannot inspect worktrees.",
+        });
+      const { caller } = yield* readCaller();
+      const projects = yield* Project.ProjectService;
+      const project = yield* projects.getById(caller.projectId).pipe(Effect.mapError(unavailable));
+      if (Option.isNone(project))
+        return yield* new OrchestratorMcpFailure({
+          code: "invalid_request",
+          message: "The project was not found.",
+        });
+      const git = yield* GitWorkflow.GitWorkflowService;
+      return yield* git
+        .listRefs({ ...input, cwd: caller.worktreePath ?? project.value.workspaceRoot })
+        .pipe(Effect.mapError(unavailable));
+    }),

apps/server/src/mcp/toolkits/worktree/handlers.ts · right 13–33

M
macroscopeappBotSep 7, 2026

This handler carries real backend behavior (capability gate, project lookup / not-found path, and the caller.worktreePath ?? project.workspaceRoot cwd choice) but no focused test covers it. The sibling worktree tools keep this logic in WorktreeMcpService, which is tested in WorktreeMcpService.test.ts with Layer.mock test layers for ThreadManagementService / ProjectService / GitWorkflowService.

Consider either moving the resolution logic into WorktreeMcpService (e.g. a listRefs(scope, input) operation) and covering it in that existing harness, or adding a focused handler test that provides test layers for ProjectService and GitWorkflowService and asserts the capability denial, project-not-found, and worktree-vs-workspace-root cwd cases.

Posted via Macroscope — Effect Service Conventions

J
Julius MarmingeSep 7, 2026

This repeats the earlier per-tool test request. The maintainer asked to avoid moving wrapper plumbing into new service methods or duplicating service tests just to expose a tool. The existing branch-backed listRefs operation is reused, with caller capability/project lookup and worktreePath-or-workspaceRoot forwarding. Shared MCP schemas/scope/errors and the readonly allowlist are tested; existing GitWorkflowService tests own the Git behavior. No concrete incorrect branch/path behavior is identified here, and no new WorktreeMcpService API is being added.

M
macroscopeappBotSep 7, 2026

The handler-specific capability, project-not-found, and cwd-selection behavior remains untested: t3_worktree_list resolves these directly in handlers.ts and does not use WorktreeMcpService; existing listRefs mocks only cover handoff. Shared schema/service coverage does not exercise that wrapper path.

apps/server/src/mcp/toolkits/worktree/handlers.ts · Earlier revision 18c4af6a · right 13–33

Original diff
@@ -1,10 +1,36 @@
+import { OrchestratorMcpFailure } from "@t3tools/contracts";
+import * as Option from "effect/Option";
+import * as GitWorkflow from "../../../git/GitWorkflowService.ts";
+import * as Project from "../../../project/ProjectService.ts";
+import { readCaller, unavailable } from "../../threadAccess.ts";
 import * as Effect from "effect/Effect";
 
 import { McpInvocationContext } from "../../McpInvocationContext.ts";
 import { WorktreeMcpService } from "../../WorktreeMcpService.ts";
 import { WorktreeToolkit } from "./tools.ts";
 
 const handlers = {
+  t3_worktree_list: (input) =>
+    Effect.gen(function* () {
+      const context = yield* McpInvocationContext;
+      if (!context.capabilities.has("worktree"))
+        return yield* new OrchestratorMcpFailure({
+          code: "capability_denied",
+          message: "This credential cannot inspect worktrees.",
+        });
+      const { caller } = yield* readCaller();
+      const projects = yield* Project.ProjectService;
+      const project = yield* projects.getById(caller.projectId).pipe(Effect.mapError(unavailable));
+      if (Option.isNone(project))
+        return yield* new OrchestratorMcpFailure({
+          code: "invalid_request",
+          message: "The project was not found.",
+        });
+      const git = yield* GitWorkflow.GitWorkflowService;
+      return yield* git
+        .listRefs({ ...input, cwd: caller.worktreePath ?? project.value.workspaceRoot })
+        .pipe(Effect.mapError(unavailable));
+    }),

apps/server/src/mcp/toolkits/worktree/handlers.ts · right 13–33

M
macroscopeappBotSep 7, 2026

This handler adds new backend behavior (capability gate, project lookup, worktree-vs-workspace cwd selection, GitWorkflowService.listRefs delegation) but nothing exercises it — the only test touched by this PR is the Claude adapter's tool-name list, and t3_worktree_list is not covered by WorktreeMcpService.test.ts either.

Consider adding a focused test that calls the tool through the toolkit with test layers for the external services only, following the pattern in apps/server/src/mcp/toolkits/core.test.ts (Layer.mock(ProjectService) / Layer.mock(GitWorkflowService) / Layer.mock(ThreadManagementService) plus Effect.provideService(McpInvocationContext, scope)), asserting at least: capability_denied without the worktree capability, invalid_request when the project is absent, and that cwd resolves to caller.worktreePath when set and to project.workspaceRoot otherwise.

Posted via Macroscope — Effect Service Conventions

J
Julius MarmingeSep 7, 2026

The maintainer asked for thin wrappers with shared MCP tests rather than a per-tool forwarding suite. This handler retains the credential capability check, resolves the existing project record, selects the existing worktree path or project workspaceRoot, then calls GitWorkflowService.listRefs unchanged. Shared object-schema/registration/scope/error tests and the annotation-derived allowlist check cover the MCP boundary; Git behavior remains in the existing service tests. No detached inventory, path-identity resolver, Git mutation or checkout state machine is added here. No concrete incorrect behavior was identified in this comment.