diff --git a/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts b/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts index e085feb2a155d..ceaef41d459f3 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts @@ -191,7 +191,7 @@ const makeHarness = Effect.fn("makePullRequestsToolkitHarness")(function* ( }), Layer.mock(Orchestrator.OrchestratorV2)({ getThreadShell: (id) => - Effect.succeed(id === THREAD_ID && thread ? v2PullRequestThread(thread) : null), + Effect.succeed(id === thread?.id ? v2PullRequestThread(thread) : null), dispatch, }), Layer.succeed(SecureRandom.SecureRandom, testCrypto), @@ -237,6 +237,21 @@ describe("pull request toolkit handlers", () => { }), ); + it.effect("points a bare thread uuid at the mcp: thread it belongs to", () => + Effect.gen(function* () { + const bareId = ThreadId.make("5d0b4a3e-8c1f-4a52-9d37-2f6c7e1b9a04"); + const harness = yield* makeHarness({ + thread: { ...makeThread([]), id: ThreadId.make(`mcp:${bareId}`) }, + }); + const error = yield* harness + .call("link_pull_request", { threadId: bareId, number: 123 }) + .pipe(Effect.flip); + expect(error.message).toBe(`Thread ${bareId} was not found. Did you mean \`mcp:${bareId}\`?`); + expect(error).toMatchObject({ _tag: "OrchestratorMcpFailure", code: "thread_not_found" }); + expect(yield* Ref.get(harness.commands)).toEqual([]); + }), + ); + it.effect("links by URL with source agent on the token's thread", () => Effect.gen(function* () { const harness = yield* makeHarness(); diff --git a/apps/server/src/mcp/toolkits/pullRequests/handlers.ts b/apps/server/src/mcp/toolkits/pullRequests/handlers.ts index 33ce2a0b84bdb..a703a8f5f39fb 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/handlers.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/handlers.ts @@ -27,6 +27,7 @@ import { } from "../../../sourceControl/threadPullRequests.ts"; import * as McpInvocationContext from "../../McpInvocationContext.ts"; import * as McpToolAccess from "../../McpToolAccess.ts"; +import { threadNotFound } from "../../threadAccess.ts"; import { type ListThreadPullRequestsResult, PullRequestLinkFailedError, @@ -40,7 +41,6 @@ import { type PullRequestTargetInput, PullRequestWatchFailedError, PullRequestWatchFromSubagentError, - PullRequestThreadNotFoundError, PullRequestThreadRequiredError, PullRequestGuideFailedError, PullRequestProjectNotFoundError, @@ -226,7 +226,7 @@ const make = Effect.gen(function* () { Effect.mapError((cause) => new Failure({ cause })), ); if (Option.isNone(thread) || thread.value.deletedAt !== null) { - return yield* new PullRequestThreadNotFoundError({ threadId }); + return yield* threadNotFound(engine, threadId, `Thread ${threadId} was not found.`); } return thread.value; }); diff --git a/apps/server/src/mcp/toolkits/pullRequests/tools.ts b/apps/server/src/mcp/toolkits/pullRequests/tools.ts index 789e9fe125235..e86bf3013bc92 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/tools.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/tools.ts @@ -104,15 +104,6 @@ export class PullRequestThreadRequiredError extends Schema.TaggedError()( - "PullRequestThreadNotFoundError", - { threadId: Schema.String }, -) { - override get message(): string { - return `Thread ${this.threadId} was not found.`; - } -} - export class PullRequestLinkFailedError extends Schema.TaggedError()( "PullRequestLinkFailedError", { cause: Schema.Defect() }, @@ -193,7 +184,6 @@ export const PullRequestToolError = Schema.Union([ PullRequestHostRequiredError, PullRequestUrlUnavailableError, PullRequestThreadRequiredError, - PullRequestThreadNotFoundError, PullRequestLinkFailedError, PullRequestUnlinkFailedError, PullRequestListFailedError,