Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions apps/server/src/mcp/toolkits/worktree/handlers.ts
Original file line number Diff line number Diff line change
@@ -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));
}),
Comment thread
juliusmarminge marked this conversation as resolved.
Comment thread
juliusmarminge marked this conversation as resolved.
t3_worktree_handoff: (input) =>
Effect.gen(function* () {
const scope = yield* McpInvocationContext;
Expand Down
35 changes: 34 additions & 1 deletion apps/server/src/mcp/toolkits/worktree/tools.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import {
WorktreeMcpFailure,
OrchestratorMcpFailure,
VcsListRefsInput,
VcsListRefsResult,
WorktreeMcpHandoffInput,
WorktreeMcpHandoffResult,
WorktreeMcpStatusResult,
} from "@t3tools/contracts";
import * as Schema from "effect/Schema";
import { GitWorkflowService } from "../../../git/GitWorkflowService.ts";
import { ProjectService } from "../../../project/ProjectService.ts";
import { ThreadManagementService } from "../../../orchestration-v2/ThreadManagementService.ts";
import { Tool, Toolkit } from "effect/unstable/ai";

import * as McpInvocationContext from "../../McpInvocationContext.ts";
Expand Down Expand Up @@ -44,4 +51,30 @@ export const WorktreeStatusTool = Tool.make("t3_worktree_status", {
.annotate(Tool.Idempotent, true)
.annotate(Tool.OpenWorld, false);

export const WorktreeToolkit = Toolkit.make(WorktreeHandoffTool, WorktreeStatusTool);
export const WorktreeListTool = Tool.make("t3_worktree_list", {
description:
"List branch refs and their associated checkout paths for this thread's workspace using the app's ref inventory. Detached worktrees without a branch are not included. Use t3_worktree_status for the thread binding and t3_worktree_handoff to create a new worktree.",
parameters: Schema.Struct({
query: VcsListRefsInput.fields.query,
cursor: VcsListRefsInput.fields.cursor,
limit: VcsListRefsInput.fields.limit,
refKind: VcsListRefsInput.fields.refKind,
includeMatchingRemoteRefs: VcsListRefsInput.fields.includeMatchingRemoteRefs,
}),
success: VcsListRefsResult,
failure: OrchestratorMcpFailure,
failureMode: "return",
dependencies: [
McpInvocationContext.McpInvocationContext,
ThreadManagementService,
ProjectService,
GitWorkflowService,
],
})
.annotate(Tool.Readonly, true)
.annotate(Tool.Destructive, false);
export const WorktreeToolkit = Toolkit.make(
WorktreeHandoffTool,
WorktreeStatusTool,
WorktreeListTool,
);
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import * as McpProviderSession from "../../mcp/McpProviderSession.ts";
import { PreviewControlsToolkit } from "../../mcp/toolkits/previewControls/tools.ts";
import { EnvironmentToolkit } from "../../mcp/toolkits/environment/tools.ts";
import { ProjectToolkit } from "../../mcp/toolkits/project/tools.ts";
import { WorktreeToolkit } from "../../mcp/toolkits/worktree/tools.ts";
import { ThreadToolkit } from "../../mcp/toolkits/thread/tools.ts";
import { OrchestratorToolkit } from "../../mcp/toolkits/orchestrator/tools.ts";
import type { EventNdjsonLogger } from "../../provider/Layers/EventNdjsonLogger.ts";
Expand Down Expand Up @@ -587,6 +588,7 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {
const readOnlyToolNames = [
...Object.values(OrchestratorToolkit.tools),
...Object.values(ThreadToolkit.tools),
...Object.values(WorktreeToolkit.tools),
...Object.values(ProjectToolkit.tools),
...Object.values(EnvironmentToolkit.tools),
...Object.values(PreviewControlsToolkit.tools),
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,8 @@ export const CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS: ReadonlyArray<string> = [
"mcp__t3-code__t3_pending_request_read",
"mcp__t3-code__t3_thread_configuration",
"mcp__t3-code__t3_thread_transfers",
"mcp__t3-code__t3_worktree_status",
"mcp__t3-code__t3_worktree_list",
"mcp__t3-code__t3_project_list",
"mcp__t3-code__t3_project_read",
"mcp__t3-code__t3_thread_search",
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/t3McpToolPresentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const T3_MCP_TOOLS: Record<
t3_thread_wait: { displayName: "Wait for a T3 thread", summaryAction: "thread-wait" },
t3_thread_interrupt: { displayName: "Interrupt a T3 thread", summaryAction: "thread-interrupt" },
t3_worktree_handoff: { displayName: "Hand off thread to a git worktree" },
t3_worktree_list: { displayName: "List workspace branches" },
t3_worktree_status: { displayName: "Get thread worktree status" },
t3_preview_list: { displayName: "List preview tabs" },
t3_preview_close: { displayName: "Close a preview tab" },
Expand Down
Loading