From 6342c1cb4b4f4484047fb16175163020b27dbab2 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 7 Sep 2026 12:10:30 -0700 Subject: [PATCH] feat(mcp): expose existing thread search --- apps/server/src/mcp/toolkits/thread/handlers.ts | 16 +++++++++++++++- apps/server/src/mcp/toolkits/thread/tools.ts | 15 +++++++++++++++ .../orchestration-v2/Adapters/ClaudeAdapterV2.ts | 1 + packages/shared/src/t3McpToolPresentation.ts | 1 + 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/apps/server/src/mcp/toolkits/thread/handlers.ts b/apps/server/src/mcp/toolkits/thread/handlers.ts index ce709fafe58cb..d5c3cabc5dc42 100644 --- a/apps/server/src/mcp/toolkits/thread/handlers.ts +++ b/apps/server/src/mcp/toolkits/thread/handlers.ts @@ -10,7 +10,14 @@ import { import * as Effect from "effect/Effect"; import { modelSelectionCommandType } from "@t3tools/shared/model"; -import { newCommandId, readThread, readWritableThread, unavailable } from "../../threadAccess.ts"; +import { + newCommandId, + readCaller, + readThread, + readWritableThread, + unavailable, +} from "../../threadAccess.ts"; +import * as ProjectionSnapshotQuery from "../../../orchestration/Services/ProjectionSnapshotQuery.ts"; import { queuedRunsInDeliveryOrder } from "../../../orchestration-v2/QueuedRunOrder.ts"; import { ThreadToolkit } from "./tools.ts"; @@ -61,6 +68,13 @@ const readQuestion = Effect.fn("mcp.readQuestion")(function* ( return { ...context, request, item }; }); export const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({ + t3_thread_search: (input) => + Effect.gen(function* () { + const { caller } = yield* readCaller(); + const query = yield* ProjectionSnapshotQuery.ProjectionSnapshotQuery; + const result = yield* query.searchThreads(input).pipe(Effect.mapError(unavailable)); + return { matches: result.matches.filter((match) => match.projectId === caller.projectId) }; + }), t3_thread_fork: (input) => Effect.gen(function* () { const { threads, projection } = yield* readWritableThread(); diff --git a/apps/server/src/mcp/toolkits/thread/tools.ts b/apps/server/src/mcp/toolkits/thread/tools.ts index 4cc50ee9c60b9..03f5bfd97f267 100644 --- a/apps/server/src/mcp/toolkits/thread/tools.ts +++ b/apps/server/src/mcp/toolkits/thread/tools.ts @@ -1,4 +1,6 @@ import { + OrchestrationSearchThreadsInput, + OrchestrationSearchThreadsResult, OrchestrationV2ThreadForkSourcePoint, OrchestrationV2ContextTransfer, TrimmedNonEmptyString, @@ -18,6 +20,7 @@ import * as Crypto from "effect/Crypto"; import * as Schema from "effect/Schema"; import { Tool, Toolkit } from "effect/unstable/ai"; +import { ProjectionSnapshotQuery } from "../../../orchestration/Services/ProjectionSnapshotQuery.ts"; import { ThreadManagementService } from "../../../orchestration-v2/ThreadManagementService.ts"; import { McpInvocationContext } from "../../McpInvocationContext.ts"; @@ -216,7 +219,19 @@ export const ThreadTransfersTool = Tool.make("t3_thread_transfers", { .annotate(Tool.Readonly, true) .annotate(Tool.Destructive, false); +export const ThreadSearchTool = Tool.make("t3_thread_search", { + ...commandTool, + description: + "Search active thread titles and content with the app's existing bounded search. Returns matches in the calling project from the global top matches; other-project matches are omitted, so this may return fewer than limit. No pagination or exhaustive-result guarantee.", + parameters: OrchestrationSearchThreadsInput, + success: OrchestrationSearchThreadsResult, + dependencies: [...commandTool.dependencies, ProjectionSnapshotQuery], +}) + .annotate(Tool.Readonly, true) + .annotate(Tool.Destructive, false); + export const ThreadToolkit = Toolkit.make( + ThreadSearchTool, ThreadForkTool, ThreadMergeBackTool, ThreadTransfersTool, diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 706063fb2dff3..3e96378354aec 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -812,6 +812,7 @@ export const CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS: ReadonlyArray = [ "mcp__t3-code__t3_pending_request_read", "mcp__t3-code__t3_thread_configuration", "mcp__t3-code__t3_thread_transfers", + "mcp__t3-code__t3_thread_search", "mcp__t3-code__t3_preview_list", "mcp__t3-code__t3_environment_read", "mcp__t3-code__t3_queue_list", diff --git a/packages/shared/src/t3McpToolPresentation.ts b/packages/shared/src/t3McpToolPresentation.ts index 29c5cd0ef4b90..3b4a62f168b42 100644 --- a/packages/shared/src/t3McpToolPresentation.ts +++ b/packages/shared/src/t3McpToolPresentation.ts @@ -61,6 +61,7 @@ const T3_MCP_TOOLS: Record< t3_thread_configure: { displayName: "Set thread model" }, t3_thread_fork: { displayName: "Fork this thread" }, t3_thread_merge_back: { displayName: "Merge thread context" }, + t3_thread_search: { displayName: "Search thread content" }, t3_thread_transfers: { displayName: "Read thread transfers" }, t3_thread_organize: { displayName: "Organize a thread" }, t3_thread_update: { displayName: "Update T3 thread metadata" },