# Files changed · PR \#10562 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/10562/diffs)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

[feat\(mcp\): expose scheduled task run-now](https://git.cafe/pingdotgg/t3code/pull/10562.md)

Version: 4

Head commit: 06de99030122b641312c452007302d6bd8f99887

Base commit: 6342c1cb4b4f4484047fb16175163020b27dbab2

Comparison base: 6342c1cb4b4f4484047fb16175163020b27dbab2

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/server/src/mcp/toolkits/thread/handlers.ts

Status: modified; +32; −0

```diff
@@ -13,11 +13,13 @@
 import {
   newCommandId,
   readCaller,
+  readMutationCaller,
   readThread,
   readWritableThread,
   unavailable,
 } from "../../threadAccess.ts";
 import * as ProjectionSnapshotQuery from "../../../orchestration/Services/ProjectionSnapshotQuery.ts";
+import * as ScheduledTasks from "../../../scheduledTasks/ScheduledTaskService.ts";
 import { queuedRunsInDeliveryOrder } from "../../../orchestration-v2/QueuedRunOrder.ts";
 import { ThreadToolkit } from "./tools.ts";
 
@@ -68,6 +70,36 @@
   return { ...context, request, item };
 });
 export const ThreadToolkitHandlersLive = ThreadToolkit.toLayer({
+  run_scheduled_task_now: (input) =>
+    Effect.gen(function* () {
+      const { caller } = yield* readMutationCaller();
+      if (
+        caller.archivedAt !== null ||
+        caller.runtimeMode !== "full-access" ||
+        caller.interactionMode !== "default"
+      )
+        return yield* new OrchestratorMcpFailure({
+          code: "capability_denied",
+          message: "Running a scheduled task requires a live full-access/default thread.",
+        });
+      const scheduler = yield* ScheduledTasks.ScheduledTaskService;
+      const { tasks } = yield* scheduler.list().pipe(Effect.mapError(unavailable));
+      if (!tasks.some((task) => task.id === input.taskId && task.projectId === caller.projectId))
+        return yield* new OrchestratorMcpFailure({
+          code: "invalid_request",
+          message: "The task was not found in the calling project.",
+        });
+      const { task } = yield* scheduler
+        .runNow({ id: input.taskId })
+        .pipe(Effect.mapError(unavailable));
+      return {
+        taskId: task.id,
+        threadId: task.threadId,
+        lastRunStatus: task.lastRunStatus,
+        runCount: task.runCount,
+        nextRunAt: task.nextRunAt,
+      };
+    }),
   t3_thread_search: (input) =>
     Effect.gen(function* () {
       const { caller } = yield* readCaller();
```

## apps/server/src/mcp/toolkits/thread/tools.ts

Status: modified; +21; −0

```diff
@@ -1,4 +1,6 @@
 import {
+  ScheduledTaskId,
+  ScheduledTask,
   OrchestrationSearchThreadsInput,
   OrchestrationSearchThreadsResult,
   OrchestrationV2ThreadForkSourcePoint,
@@ -21,6 +23,7 @@
 import { Tool, Toolkit } from "effect/unstable/ai";
 
 import { ProjectionSnapshotQuery } from "../../../orchestration/Services/ProjectionSnapshotQuery.ts";
+import { ScheduledTaskService } from "../../../scheduledTasks/ScheduledTaskService.ts";
 import { ThreadManagementService } from "../../../orchestration-v2/ThreadManagementService.ts";
 import { McpInvocationContext } from "../../McpInvocationContext.ts";
 
@@ -230,7 +233,25 @@
   .annotate(Tool.Readonly, true)
   .annotate(Tool.Destructive, false);
 
+export const ScheduledTaskRunTool = Tool.make("run_scheduled_task_now", {
+  ...commandTool,
+  description:
+    "Run a scheduled task in the calling project now through the existing scheduler. Requires a full-access/default caller. Each call is a new manual run; completion means dispatch/bookkeeping completed, not that the provider turn finished.",
+  parameters: Schema.Struct({ taskId: ScheduledTaskId }),
+  success: Schema.Struct({
+    taskId: ScheduledTaskId,
+    threadId: ScheduledTask.fields.threadId,
+    lastRunStatus: ScheduledTask.fields.lastRunStatus,
+    runCount: NonNegativeInt,
+    nextRunAt: ScheduledTask.fields.nextRunAt,
+  }),
+  dependencies: [...commandTool.dependencies, ScheduledTaskService],
+})
+  .annotate(Tool.Destructive, true)
+  .annotate(Tool.OpenWorld, true);
+
 export const ThreadToolkit = Toolkit.make(
+  ScheduledTaskRunTool,
   ThreadSearchTool,
   ThreadForkTool,
   ThreadMergeBackTool,
```

## packages/shared/src/t3McpToolPresentation.ts

Status: modified; +1; −0

```diff
@@ -34,6 +34,7 @@
   delegate_task: { displayName: "Delegate a child task", summaryAction: "delegate" },
   task_status: { displayName: "Get delegated task status", summaryAction: "task-status" },
   task_cancel: { displayName: "Cancel delegated task", summaryAction: "task-cancel" },
+  run_scheduled_task_now: { displayName: "Run scheduled task now" },
   schedule_task: { displayName: "Schedule a recurring task", summaryAction: "schedule-create" },
   list_scheduled_tasks: { displayName: "List scheduled tasks", summaryAction: "schedule-list" },
   update_scheduled_task: {
```
