Skip to content
Open
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
9 changes: 9 additions & 0 deletions apps/server/src/mcp/McpHttpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import * as McpInvocationContext from "./McpInvocationContext.ts";
import * as OrchestratorMcpService from "./OrchestratorMcpService.ts";
import * as McpSessionRegistry from "./McpSessionRegistry.ts";
import * as PreviewAutomationBroker from "./PreviewAutomationBroker.ts";
import * as PendingRequestMcpService from "./PendingRequestMcpService.ts";
import { OrchestratorToolkitHandlersLive } from "./toolkits/orchestrator/handlers.ts";
import { OrchestratorToolkit } from "./toolkits/orchestrator/tools.ts";
import {
Expand All @@ -25,6 +26,8 @@ import {
PreviewSnapshotToolkit,
PreviewStandardToolkit,
} from "./toolkits/preview/tools.ts";
import { PendingRequestToolkitHandlersLive } from "./toolkits/pendingRequest/handlers.ts";
import { PendingRequestToolkit } from "./toolkits/pendingRequest/tools.ts";
import { WorktreeToolkitHandlersLive } from "./toolkits/worktree/handlers.ts";
import { WorktreeToolkit } from "./toolkits/worktree/tools.ts";
import * as WorktreeMcpService from "./WorktreeMcpService.ts";
Expand Down Expand Up @@ -227,6 +230,11 @@ export const OrchestratorToolkitRegistrationLive = McpServer.toolkit(Orchestrato
Layer.provide(OrchestratorMcpService.layer),
);

export const PendingRequestToolkitRegistrationLive = McpServer.toolkit(PendingRequestToolkit).pipe(
Layer.provide(PendingRequestToolkitHandlersLive),
Layer.provide(PendingRequestMcpService.layer),
);

export const WorktreeToolkitRegistrationLive = McpServer.toolkit(WorktreeToolkit).pipe(
Layer.provide(WorktreeToolkitHandlersLive),
Layer.provide(WorktreeMcpService.layer),
Expand All @@ -242,5 +250,6 @@ const McpTransportLive = McpServer.layerHttp({
export const layer = Layer.mergeAll(
PreviewToolkitRegistrationLive,
OrchestratorToolkitRegistrationLive,
PendingRequestToolkitRegistrationLive,
WorktreeToolkitRegistrationLive,
).pipe(Layer.provideMerge(McpTransportLive));
32 changes: 32 additions & 0 deletions apps/server/src/mcp/McpModeCeilings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { ProviderInteractionMode, RuntimeMode } from "@t3tools/contracts";

function runtimeModeRank(mode: RuntimeMode): number {
switch (mode) {
case "approval-required":
return 0;
case "auto-accept-edits":
return 1;
case "auto":
return 2;
case "full-access":
return 3;
}
}

function interactionModeRank(mode: ProviderInteractionMode): number {
return mode === "plan" ? 0 : 1;
}

export function runtimeModeWithinMcpCeiling(
callerMode: RuntimeMode,
targetMode: RuntimeMode,
): boolean {
return runtimeModeRank(targetMode) <= runtimeModeRank(callerMode);
}

export function interactionModeWithinMcpCeiling(
callerMode: ProviderInteractionMode,
targetMode: ProviderInteractionMode,
): boolean {
return interactionModeRank(targetMode) <= interactionModeRank(callerMode);
}
23 changes: 4 additions & 19 deletions apps/server/src/mcp/OrchestratorMcpService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
} from "../orchestration-v2/ThreadManagementService.ts";
import { ProviderRegistry } from "../provider/Services/ProviderRegistry.ts";
import { ScheduledTaskService } from "../scheduledTasks/ScheduledTaskService.ts";
import { interactionModeWithinMcpCeiling, runtimeModeWithinMcpCeiling } from "./McpModeCeilings.ts";
import type { McpInvocationScope } from "./McpInvocationContext.ts";

const DEFAULT_WAIT_TIMEOUT_MS = 10 * 60 * 1_000;
Expand Down Expand Up @@ -371,29 +372,12 @@ function pageIncludesTerminalTaskResult(input: {
});
}

function runtimeModeRank(mode: RuntimeMode): number {
switch (mode) {
case "approval-required":
return 0;
case "auto-accept-edits":
return 1;
case "auto":
return 2;
case "full-access":
return 3;
}
}

function interactionModeRank(mode: ProviderInteractionMode): number {
return mode === "plan" ? 0 : 1;
}

function resolveRuntimeMode(
parentMode: RuntimeMode,
requested: OrchestratorMcpRuntimeMode | undefined,
): Effect.Effect<RuntimeMode, OrchestratorMcpFailure> {
const resolved = requested === undefined || requested === "inherit" ? parentMode : requested;
return runtimeModeRank(resolved) > runtimeModeRank(parentMode)
return !runtimeModeWithinMcpCeiling(parentMode, resolved)
? Effect.fail(
failure(
"runtime_mode_escalation_denied",
Expand All @@ -408,7 +392,7 @@ function resolveInteractionMode(
requested: OrchestratorMcpInteractionMode | undefined,
): Effect.Effect<ProviderInteractionMode, OrchestratorMcpFailure> {
const resolved = requested === undefined || requested === "inherit" ? parentMode : requested;
return interactionModeRank(resolved) > interactionModeRank(parentMode)
return !interactionModeWithinMcpCeiling(parentMode, resolved)
? Effect.fail(
failure(
"interaction_mode_escalation_denied",
Expand Down Expand Up @@ -1113,6 +1097,7 @@ const make = Effect.gen(function* () {
threadManagement: true,
incrementalThreadRead: true,
scheduledTasks: true,
delegatedUserInputRequests: true,
maxBatchThreads: 20,
},
};
Expand Down
Loading
Loading