Thread transfer impact
ā ļø The latest CI run did not produce a thread transfer result for bc357ee.
This comment will update automatically after the next completed run.
Part 13/16 of the shared-core and MCP stack. Based on #10562. Next: #10564.
Expose project list/read/create/update/delete and repository clone using ProjectService and SourceControlRepositoryService.
Mutations require a full-access/default caller. Clone and registration are separate operations; no lost-result adoption, journal or custom cascade/count implementation. Existing service validation and deletion behavior remain authoritative.
Create/update inputs now use the shared ProjectCreatePayload and ProjectUpdatePayload schemas also used by RPC and ProjectService.
MCP-only rebuild of #8677, preserving attribution to Julius Marminge's original work. Original branches remain available for separate service follow-ups.
Validation: The composed stack passes 94 tests across 12 focused files, including shared core MCP and real MCP/V2 integration, attachment intake, project RPC/service contracts, and client model command selection. Server/contracts/shared/client-runtime typechecks and targeted format/lint/diff checks pass. New behavior coverage lives with the shared operation; no per-tool mock suite was added. Current-head CI is shown below.
Layer size: 7 files, +196/-0. No domain-service production implementation or documentation files change in this MCP layer.
Prepared with Codex in the OpenAI agent runtime.
Note
ProjectToolkit exposing t3_project_list, t3_project_read, t3_project_create, t3_project_update, t3_project_delete, and t3_project_clone.mutation access effect in handlers.ts rejects archived threads, non-full-access modes, and non-default interaction modes with a capability-denied failure.Macroscope summarized bc357ee.
ā ļø The latest CI run did not produce a thread transfer result for bc357ee.
This comment will update automatically after the next completed run.
| ⦠| ⦠| @@ -0,0 +1,75 @@ | |
| 26 | + | export const ProjectHandlersLive = ProjectToolkit.toLayer({ | |
| 27 | + | t3_project_list: (input) => | |
| 28 | + | Effect.gen(function* () { | |
| 29 | + | const projects = yield* access; | |
š“ Critical project/handlers.ts:29
These handlers expose every project to a caller: t3_project_list returns the global snapshot, while t3_project_read, t3_project_update, and t3_project_delete accept arbitrary projectId values. Because access discards caller.projectId, a thread authenticated for project A can read, modify, or delete project B; retain and enforce caller.projectId when listing and targeting projects.
| 1 | In file @apps/server/src/mcp/toolkits/project/handlers.ts around line 29: |
| 2 | |
| 3 | These handlers expose every project to a caller: `t3_project_list` returns the global snapshot, while `t3_project_read`, `t3_project_update`, and `t3_project_delete` accept arbitrary `projectId` values. Because `access` discards `caller.projectId`, a thread authenticated for project A can read, modify, or delete project B; retain and enforce `caller.projectId` when listing and targeting projects. |
Environment-wide project management is intentional, not a missed current-project filter. The requested feature includes listing/managing projects and launching into another selected project, and the new tool descriptions explicitly say "in this environment". McpInvocationScope carries an environment/session capability, not a per-project ACL; caller.projectId remains the scope for ordinary thread tools in readThread. Project mutations separately require a live full-access/default caller through readMutationCaller. Restricting every project operation to caller.projectId would remove the requested project-management functionality. This PR does not claim a per-project credential authorization model.
Verdict: Not approved
Macroscope's review found this PR not approvable ā This PR adds six production MCP operations for listing, creating, updating, deleting projects, and cloning repositories, including persistent, filesystem, git, and potentially cascading deletion effects. The live server registration and expanded read-only tool availability make this a substantial new workflow rather than a small isolated change.
Notes:
No code changes detected at bc357ee. Prior analysis still applies.
You can add or adjust custom eligibility rules. Learn more.
Non-blocking: the new tools add real backend behavior (list pagination, the mutation capability gate, delete/clone gating) but the added tests only cover registration and tool-name uniqueness. A focused project/handlers.test.ts ā in the style of preview/handlers.test.ts, with test layers for ProjectService/SourceControlRepositoryService only ā would cover the gate and paging edges.
Posted via Macroscope ā Effect Service Conventions
| ⦠| ⦠| @@ -0,0 +1,75 @@ | |
| 70 | + | Effect.gen(function* () { | |
| 71 | + | yield* mutation; | |
| 72 | + | const repositories = yield* Repositories.SourceControlRepositoryService; | |
| 73 | + | return yield* repositories.cloneRepository(input).pipe(Effect.mapError(unavailable)); | |
create, update, delete, and cloneRepository map their whole error channel through unavailable(), so every failure reaches the caller as orchestration_error / "The operation could not be completed." That erases the structured domain errors these calls are documented to produce ā ProjectConflictError ("an existing registered workspace is rejected"), ProjectNotEmptyError ("Nonempty projects require force=true"), ProjectNotFoundError, and the destination-path variants of SourceControlRepositoryError ā leaving the agent unable to distinguish a bad request from an infrastructure fault.
Consider mapping the known tags where the context is known and reserving unavailable() for the genuinely unknown/lower-level failure (ProjectOperationError), the way threadManagementFailure in OrchestratorMcpService.ts does:
| 1 | projects.create({ ...input, commandId, projectId: ProjectId.make(commandId) }).pipe( |
| 2 | Effect.catchTags({ |
| 3 | ProjectNotFoundError: (error) => |
| 4 | Effect.fail(new OrchestratorMcpFailure({ code: "invalid_request", message: error.message })), |
| 5 | ProjectConflictError: (error) => |
| 6 | Effect.fail(new OrchestratorMcpFailure({ code: "invalid_request", message: error.message })), |
| 7 | ProjectNotEmptyError: (error) => |
| 8 | Effect.fail(new OrchestratorMcpFailure({ code: "invalid_request", message: error.message })), |
| 9 | ProjectOperationError: () => Effect.fail(unavailable()), |
| 10 | }), |
| 11 | ) |
The same applies to the clone handler, where SourceControlRepositoryError already carries a caller-safe detail.
Posted via Macroscope ā Effect Service Conventions
Fixed in cbb362ac5458cc1951a41f5a381a467b971b39d9. Known ProjectNotFound/Conflict/NotEmpty errors now return invalid_request with fixed actionable messages, while ProjectOperationError remains the bounded generic service failure. Clone returns SourceControlRepositoryError.detail, whose production constructors use safe fixed explanations; nested causes are never serialized. No service error classes or lifecycle logic changed. Composed shared tests 6/6, server typecheck, targeted lint/format/diff checks pass.
Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.
A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.
Added 3 commits
Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.
A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.