diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index e22a5f0d3..c147ac193 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -84,6 +84,9 @@ export const dict = { "cron.error.interval.min": "Interval must be at least 1 minute", "cron.error.timeout.min": "Timeout must be at least 1 minute", "cron.error.validation.fix": "Please fix the highlighted fields", + "cron.field.mcpServers": "MCP servers", + "cron.field.mcpServers.empty": "No MCP servers configured. Add servers in Settings > MCP.", + "cron.field.mcpServers.help": "Only enabled MCP servers will be available to this task. By default all servers are off.", "cron.rail.title": "Sessions", "cron.rail.tasks": "Scheduled tasks", "cron.rail.empty": "No sessions yet", diff --git a/packages/app/src/pages/cron.tsx b/packages/app/src/pages/cron.tsx index 14243a8af..2933199ed 100644 --- a/packages/app/src/pages/cron.tsx +++ b/packages/app/src/pages/cron.tsx @@ -7,6 +7,7 @@ import { Dialog } from "@codeplane-ai/ui/dialog" import { Icon } from "@codeplane-ai/ui/icon" import { IconButton } from "@codeplane-ai/ui/icon-button" import { Select } from "@codeplane-ai/ui/select" +import { Switch } from "@codeplane-ai/ui/switch" import { Tag } from "@codeplane-ai/ui/tag" import { TextField } from "@codeplane-ai/ui/text-field" import { Tooltip } from "@codeplane-ai/ui/tooltip" @@ -394,6 +395,7 @@ type EditorState = { agent: string model: string status: CronStatus + mcpServers: string[] errors: { name?: string; prompt?: string; schedule?: string; timeout?: string; form?: string } } @@ -460,6 +462,14 @@ function CronEditorDialog(props: { return result.sort((a, b) => a.group.localeCompare(b.group) || a.label.localeCompare(b.label)) }) + const mcpServerNames = createMemo(() => { + const config = globalSync.data.config.mcp ?? {} + return Object.keys(config).filter((name) => { + const entry = config[name] + return entry && typeof entry === "object" && "type" in entry + }).sort() + }) + const initial = (): EditorState => { const e = props.existing if (!e) @@ -474,6 +484,7 @@ function CronEditorDialog(props: { agent: "", model: "", status: "active", + mcpServers: [], errors: {}, } return { @@ -488,6 +499,7 @@ function CronEditorDialog(props: { agent: e.agent ?? "", model: e.model ?? "", status: e.status, + mcpServers: e.mcpServers ?? [], errors: {}, } } @@ -562,6 +574,7 @@ function CronEditorDialog(props: { agent: store.agent.trim() || null, model: store.model.trim() || null, status: store.status, + mcpServers: store.mcpServers.length > 0 ? store.mcpServers : null, } await CronClient.update(conn, props.existing.id, body) } else { @@ -576,6 +589,7 @@ function CronEditorDialog(props: { agent: store.agent.trim() || undefined, model: store.model.trim() || undefined, status: store.status, + mcpServers: store.mcpServers.length > 0 ? store.mcpServers : undefined, } await CronClient.create(conn, body) } @@ -728,6 +742,40 @@ function CronEditorDialog(props: { onSelect={(o) => setStore("model", o ? `${o.providerID}/${o.modelID}` : "")} /> +
+ + 0} + fallback={ +
+ {language.t("cron.field.mcpServers.empty")} +
+ } + > +
+
{language.t("cron.field.mcpServers.help")}
+ + {(name) => ( +
+ {name} + { + setStore( + "mcpServers", + enabled + ? [...store.mcpServers, name] + : store.mcpServers.filter((s) => s !== name), + ) + }} + /> +
+ )} +
+
+
+
{store.errors.form} diff --git a/packages/app/src/utils/cron-client.ts b/packages/app/src/utils/cron-client.ts index 06de7e3ff..97a3b84cc 100644 --- a/packages/app/src/utils/cron-client.ts +++ b/packages/app/src/utils/cron-client.ts @@ -21,6 +21,7 @@ export type CronTask = { status: CronStatus timeoutMs?: number maxRetries?: number + mcpServers?: string[] lastRunID?: string lastRunAt?: number lastRunStatus?: CronRunStatus @@ -55,6 +56,7 @@ export type CronCreateInput = { status?: CronStatus timeoutMs?: number maxRetries?: number + mcpServers?: string[] } export type CronUpdateInput = { @@ -68,6 +70,7 @@ export type CronUpdateInput = { status?: CronStatus timeoutMs?: number | null maxRetries?: number | null + mcpServers?: string[] | null } function authHeaders(server: ServerConnection.HttpBase): Record { diff --git a/packages/codeplane/migration/20260601130626_add_mcp_servers_to_cron_tasks/migration.sql b/packages/codeplane/migration/20260601130626_add_mcp_servers_to_cron_tasks/migration.sql new file mode 100644 index 000000000..40fd4c5a7 --- /dev/null +++ b/packages/codeplane/migration/20260601130626_add_mcp_servers_to_cron_tasks/migration.sql @@ -0,0 +1,3 @@ +-- Adds cron-task-level MCP server affordances. Each scheduled task can list +-- which MCP servers should be enabled for its runs (null = all, []=none). +ALTER TABLE `cron_task` ADD COLUMN `mcp_servers` text; diff --git a/packages/codeplane/migration/20260601130626_add_mcp_servers_to_cron_tasks/snapshot.json b/packages/codeplane/migration/20260601130626_add_mcp_servers_to_cron_tasks/snapshot.json new file mode 100644 index 000000000..5d6e5f12f --- /dev/null +++ b/packages/codeplane/migration/20260601130626_add_mcp_servers_to_cron_tasks/snapshot.json @@ -0,0 +1,2178 @@ +{ + "version": "7", + "dialect": "sqlite", + "id": "290f4ee5-8c00-4a73-a40a-5a27a4ee23f1", + "prevIds": [ + "66cbe0d7-def0-451b-b88a-7608513a9b44" + ], + "ddl": [ + { + "name": "account_state", + "entityType": "tables" + }, + { + "name": "account", + "entityType": "tables" + }, + { + "name": "control_account", + "entityType": "tables" + }, + { + "name": "workspace", + "entityType": "tables" + }, + { + "name": "cron_run", + "entityType": "tables" + }, + { + "name": "cron_task", + "entityType": "tables" + }, + { + "name": "project", + "entityType": "tables" + }, + { + "name": "prompt_job", + "entityType": "tables" + }, + { + "name": "message", + "entityType": "tables" + }, + { + "name": "part", + "entityType": "tables" + }, + { + "name": "permission", + "entityType": "tables" + }, + { + "name": "session_entry", + "entityType": "tables" + }, + { + "name": "session", + "entityType": "tables" + }, + { + "name": "todo", + "entityType": "tables" + }, + { + "name": "session_share", + "entityType": "tables" + }, + { + "name": "event_sequence", + "entityType": "tables" + }, + { + "name": "event", + "entityType": "tables" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "account_state" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "active_account_id", + "entityType": "columns", + "table": "account_state" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "active_org_id", + "entityType": "columns", + "table": "account_state" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "email", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "access_token", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "refresh_token", + "entityType": "columns", + "table": "account" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "token_expiry", + "entityType": "columns", + "table": "account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "email", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "access_token", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "refresh_token", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "token_expiry", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "active", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "type", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": "''", + "generated": null, + "name": "name", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "branch", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "directory", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "extra", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "project_id", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "task_id", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "status", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "attempt", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_started", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_completed", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "error_message", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "logs", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "cron_run" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "project_id", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "directory", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "description", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "prompt", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "agent", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "model", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "schedule_kind", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "schedule_value", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "timezone", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "status", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "timeout_ms", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "max_retries", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "last_run_id", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "last_run_at", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "last_run_status", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "last_error", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "next_run_at", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "mcp_servers", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "cron_task" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "worktree", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "vcs", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "icon_url", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "icon_url_override", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "icon_color", + "entityType": "columns", + "table": "project" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "project" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "project" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_initialized", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "sandboxes", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "commands", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "directory", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "payload", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "status", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "attempt", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "max_attempts", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "next_run_at", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_started", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_completed", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "error_message", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "sort_order", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "prompt_job" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "message" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "message" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "message" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "message" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "message" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "message_id", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "part" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "part" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "project_id", + "entityType": "columns", + "table": "permission" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "permission" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "permission" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "permission" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "session_entry" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "session_entry" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "type", + "entityType": "columns", + "table": "session_entry" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "session_entry" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "session_entry" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "session_entry" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "project_id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "workspace_id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "parent_id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "slug", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "directory", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "title", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "version", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "share_url", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_additions", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_deletions", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_files", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_diffs", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "revert", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "permission", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_compacting", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_archived", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "cron_run_id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "metadata", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "content", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "status", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "priority", + "entityType": "columns", + "table": "todo" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "position", + "entityType": "columns", + "table": "todo" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "todo" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "secret", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "aggregate_id", + "entityType": "columns", + "table": "event_sequence" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "seq", + "entityType": "columns", + "table": "event_sequence" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "event" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "aggregate_id", + "entityType": "columns", + "table": "event" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "seq", + "entityType": "columns", + "table": "event" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "type", + "entityType": "columns", + "table": "event" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "event" + }, + { + "columns": [ + "active_account_id" + ], + "tableTo": "account", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "nameExplicit": false, + "name": "fk_account_state_active_account_id_account_id_fk", + "entityType": "fks", + "table": "account_state" + }, + { + "columns": [ + "project_id" + ], + "tableTo": "project", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_workspace_project_id_project_id_fk", + "entityType": "fks", + "table": "workspace" + }, + { + "columns": [ + "task_id" + ], + "tableTo": "cron_task", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_cron_run_task_id_cron_task_id_fk", + "entityType": "fks", + "table": "cron_run" + }, + { + "columns": [ + "project_id" + ], + "tableTo": "project", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_cron_task_project_id_project_id_fk", + "entityType": "fks", + "table": "cron_task" + }, + { + "columns": [ + "session_id" + ], + "tableTo": "session", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_message_session_id_session_id_fk", + "entityType": "fks", + "table": "message" + }, + { + "columns": [ + "message_id" + ], + "tableTo": "message", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_part_message_id_message_id_fk", + "entityType": "fks", + "table": "part" + }, + { + "columns": [ + "project_id" + ], + "tableTo": "project", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_permission_project_id_project_id_fk", + "entityType": "fks", + "table": "permission" + }, + { + "columns": [ + "session_id" + ], + "tableTo": "session", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_session_entry_session_id_session_id_fk", + "entityType": "fks", + "table": "session_entry" + }, + { + "columns": [ + "project_id" + ], + "tableTo": "project", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_session_project_id_project_id_fk", + "entityType": "fks", + "table": "session" + }, + { + "columns": [ + "session_id" + ], + "tableTo": "session", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_todo_session_id_session_id_fk", + "entityType": "fks", + "table": "todo" + }, + { + "columns": [ + "session_id" + ], + "tableTo": "session", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_session_share_session_id_session_id_fk", + "entityType": "fks", + "table": "session_share" + }, + { + "columns": [ + "aggregate_id" + ], + "tableTo": "event_sequence", + "columnsTo": [ + "aggregate_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_event_aggregate_id_event_sequence_aggregate_id_fk", + "entityType": "fks", + "table": "event" + }, + { + "columns": [ + "email", + "url" + ], + "nameExplicit": false, + "name": "control_account_pk", + "entityType": "pks", + "table": "control_account" + }, + { + "columns": [ + "session_id", + "position" + ], + "nameExplicit": false, + "name": "todo_pk", + "entityType": "pks", + "table": "todo" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "account_state_pk", + "table": "account_state", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "account_pk", + "table": "account", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "workspace_pk", + "table": "workspace", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "cron_run_pk", + "table": "cron_run", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "cron_task_pk", + "table": "cron_task", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "project_pk", + "table": "project", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "prompt_job_pk", + "table": "prompt_job", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "message_pk", + "table": "message", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "part_pk", + "table": "part", + "entityType": "pks" + }, + { + "columns": [ + "project_id" + ], + "nameExplicit": false, + "name": "permission_pk", + "table": "permission", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "session_entry_pk", + "table": "session_entry", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "session_pk", + "table": "session", + "entityType": "pks" + }, + { + "columns": [ + "session_id" + ], + "nameExplicit": false, + "name": "session_share_pk", + "table": "session_share", + "entityType": "pks" + }, + { + "columns": [ + "aggregate_id" + ], + "nameExplicit": false, + "name": "event_sequence_pk", + "table": "event_sequence", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "event_pk", + "table": "event", + "entityType": "pks" + }, + { + "columns": [ + { + "value": "task_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "cron_run_task_idx", + "entityType": "indexes", + "table": "cron_run" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "cron_run_session_idx", + "entityType": "indexes", + "table": "cron_run" + }, + { + "columns": [ + { + "value": "status", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "cron_run_status_idx", + "entityType": "indexes", + "table": "cron_run" + }, + { + "columns": [ + { + "value": "project_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "cron_task_project_idx", + "entityType": "indexes", + "table": "cron_task" + }, + { + "columns": [ + { + "value": "status", + "isExpression": false + }, + { + "value": "next_run_at", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "cron_task_status_next_idx", + "entityType": "indexes", + "table": "cron_task" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "prompt_job_session_idx", + "entityType": "indexes", + "table": "prompt_job" + }, + { + "columns": [ + { + "value": "status", + "isExpression": false + }, + { + "value": "next_run_at", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "prompt_job_status_next_idx", + "entityType": "indexes", + "table": "prompt_job" + }, + { + "columns": [ + { + "value": "status", + "isExpression": false + }, + { + "value": "sort_order", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "prompt_job_status_sort_idx", + "entityType": "indexes", + "table": "prompt_job" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + }, + { + "value": "time_created", + "isExpression": false + }, + { + "value": "id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "message_session_time_created_id_idx", + "entityType": "indexes", + "table": "message" + }, + { + "columns": [ + { + "value": "message_id", + "isExpression": false + }, + { + "value": "id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "part_message_id_id_idx", + "entityType": "indexes", + "table": "part" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "part_session_idx", + "entityType": "indexes", + "table": "part" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_entry_session_idx", + "entityType": "indexes", + "table": "session_entry" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + }, + { + "value": "type", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_entry_session_type_idx", + "entityType": "indexes", + "table": "session_entry" + }, + { + "columns": [ + { + "value": "time_created", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_entry_time_created_idx", + "entityType": "indexes", + "table": "session_entry" + }, + { + "columns": [ + { + "value": "project_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_project_idx", + "entityType": "indexes", + "table": "session" + }, + { + "columns": [ + { + "value": "workspace_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_workspace_idx", + "entityType": "indexes", + "table": "session" + }, + { + "columns": [ + { + "value": "parent_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_parent_idx", + "entityType": "indexes", + "table": "session" + }, + { + "columns": [ + { + "value": "cron_run_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_cron_run_idx", + "entityType": "indexes", + "table": "session" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "todo_session_idx", + "entityType": "indexes", + "table": "todo" + } + ], + "renames": [] +} \ No newline at end of file diff --git a/packages/codeplane/src/cron/cron.sql.ts b/packages/codeplane/src/cron/cron.sql.ts index 947f8a91d..67bdd9824 100644 --- a/packages/codeplane/src/cron/cron.sql.ts +++ b/packages/codeplane/src/cron/cron.sql.ts @@ -30,6 +30,7 @@ export const CronTaskTable = sqliteTable( last_run_status: text(), last_error: text(), next_run_at: integer(), + mcp_servers: text({ mode: "json" }).$type(), ...Timestamps, }, (table) => [ diff --git a/packages/codeplane/src/cron/cron.ts b/packages/codeplane/src/cron/cron.ts index a29cf9b8d..223cd2201 100644 --- a/packages/codeplane/src/cron/cron.ts +++ b/packages/codeplane/src/cron/cron.ts @@ -78,6 +78,7 @@ export const Task = Schema.Struct({ lastRunStatus: Schema.optional(RunStatus), lastError: Schema.optional(Schema.String), nextRunAt: Schema.optional(Schema.Number), + mcpServers: Schema.optional(Schema.Array(Schema.String)), time: Time, }) .annotate({ identifier: "CronTask" }) @@ -117,6 +118,7 @@ export const CreateInput = Schema.Struct({ status: Schema.optional(Status), timeoutMs: Schema.optional(Schema.Number), maxRetries: Schema.optional(Schema.Number), + mcpServers: Schema.optional(Schema.Array(Schema.String)), }).pipe(withStatics((s) => ({ zod: zod(s) }))) export type CreateInput = Types.DeepMutable> @@ -132,6 +134,7 @@ export const UpdateInput = Schema.Struct({ status: Schema.optional(Status), timeoutMs: Schema.optional(Schema.NullOr(Schema.Number)), maxRetries: Schema.optional(Schema.NullOr(Schema.Number)), + mcpServers: Schema.optional(Schema.NullOr(Schema.Array(Schema.String))), }).pipe(withStatics((s) => ({ zod: zod(s) }))) export type UpdateInput = Types.DeepMutable> @@ -192,6 +195,7 @@ export function taskFromRow(row: TaskRow): Task { lastRunStatus: (row.last_run_status as RunStatus | null) ?? undefined, lastError: row.last_error ?? undefined, nextRunAt: row.next_run_at ?? undefined, + mcpServers: row.mcp_servers ?? undefined, time: { created: row.time_created, updated: row.time_updated, @@ -444,6 +448,7 @@ export const layer = Layer.effect( last_run_status: null, last_error: null, next_run_at: nextRun, + mcp_servers: input.mcpServers ?? null, time_created: now, time_updated: now, }) @@ -474,6 +479,7 @@ export const layer = Layer.effect( if (input.timezone !== undefined) next.timezone = input.timezone ?? null if (input.timeoutMs !== undefined) next.timeout_ms = input.timeoutMs ?? null if (input.maxRetries !== undefined) next.max_retries = input.maxRetries ?? null + if (input.mcpServers !== undefined) next.mcp_servers = input.mcpServers const newSchedule = input.schedule ?? existing.schedule if (input.schedule) { diff --git a/packages/codeplane/src/cron/scheduler.ts b/packages/codeplane/src/cron/scheduler.ts index 5533e82aa..f86f7ecf6 100644 --- a/packages/codeplane/src/cron/scheduler.ts +++ b/packages/codeplane/src/cron/scheduler.ts @@ -152,6 +152,9 @@ export const layer = Layer.effect( title: `[Cron] ${task.name}`, permission: ALLOW_ALL_PERMISSIONS, cronRunID: run.id, + metadata: task.mcpServers?.length + ? { enabledMcpServers: task.mcpServers } + : undefined, }) yield* cron.recordRun({ runID: run.id, diff --git a/packages/codeplane/src/server/routes/cron.ts b/packages/codeplane/src/server/routes/cron.ts index 9919e26a4..b226db1a2 100644 --- a/packages/codeplane/src/server/routes/cron.ts +++ b/packages/codeplane/src/server/routes/cron.ts @@ -34,6 +34,7 @@ const CreateInput = z.object({ status: Cron.Status.zod.optional(), timeoutMs: z.number().optional(), maxRetries: z.number().optional(), + mcpServers: z.array(z.string()).optional(), }) const cronRuntime = makeRuntime(Cron.Service, Cron.defaultLayer) diff --git a/packages/codeplane/src/session/llm.ts b/packages/codeplane/src/session/llm.ts index e217aa51c..0448a4113 100644 --- a/packages/codeplane/src/session/llm.ts +++ b/packages/codeplane/src/session/llm.ts @@ -119,21 +119,18 @@ const live: Layer.Layer< // TODO: move this to a proper hook const isOpenaiOauth = item.id === "openai" && info?.type === "oauth" - const system: string[] = [] - system.push( + const system: string[] = [ [ // use agent prompt otherwise provider prompt ...(input.agent.prompt ? [input.agent.prompt] : SystemPrompt.provider(input.model)), // goal-mode instructions prepended to the provider prompt ...(input.agent.name === "goal" ? [PROMPT_GOAL] : []), - // any custom prompt passed into this call - ...input.system, - // any custom prompt from last user message - ...(input.user.system ? [input.user.system] : []), ] .filter((x) => x) .join("\n"), - ) + ...input.system, + ...(input.user.system ? [input.user.system] : []), + ].filter((item) => item.length > 0) const header = system[0] yield* plugin.trigger( @@ -141,11 +138,23 @@ const live: Layer.Layer< { sessionID: input.sessionID, model: input.model }, { system }, ) - // rejoin to maintain 2-part structure for caching if header unchanged - if (system.length > 2 && system[0] === header) { + // Keep the provider/agent header and the first static context block intact so + // providers that cache prompt prefixes can reuse them across turns. Any tail + // blocks after that are dynamic and can be compacted together safely. + if (system[0] === header && system.length > 3) { + const first = system[0] + const second = system[1] + const rest = system.slice(2) + system.length = 0 + if (first) system.push(first) + if (second) system.push(second) + system.push(rest.join("\n")) + } else if (system[0] === header && system.length > 2 && !system[1]) { + const first = system[0] const rest = system.slice(1) system.length = 0 - system.push(header, rest.join("\n")) + if (first) system.push(first) + system.push(rest.join("\n")) } const variant = diff --git a/packages/codeplane/src/session/prompt.ts b/packages/codeplane/src/session/prompt.ts index 0c7e07bcc..20ca8e625 100644 --- a/packages/codeplane/src/session/prompt.ts +++ b/packages/codeplane/src/session/prompt.ts @@ -646,11 +646,21 @@ NOTE: At any point in time through this workflow you should feel free to ask the agent: input.agent, sessionPermission: input.session.permission, } - const [nativeTools, nativeAvailability, mcpTools] = yield* Effect.all( + const [nativeTools, nativeAvailability, allMcpTools] = yield* Effect.all( [registry.tools(toolInput), registry.availability(toolInput), mcp.tools()], { concurrency: "unbounded" }, ) + const sanitizeMcp = (s: string) => s.replace(/[^a-zA-Z0-9_-]/g, "_") + const enabledMcpServers = input.session.metadata?.enabledMcpServers as string[] | undefined + const mcpTools = enabledMcpServers?.length + ? Object.fromEntries( + Object.entries(allMcpTools).filter(([key]) => + enabledMcpServers.some((name) => key.startsWith(sanitizeMcp(name) + "_")), + ), + ) + : allMcpTools + const context = (args: any, options: ToolExecutionOptions): Tool.Context => ({ sessionID: input.session.id, abort: options.abortSignal!, diff --git a/packages/codeplane/src/session/session.ts b/packages/codeplane/src/session/session.ts index 0b65194c6..4d056f295 100644 --- a/packages/codeplane/src/session/session.ts +++ b/packages/codeplane/src/session/session.ts @@ -446,6 +446,7 @@ export interface Interface { permission?: Permission.Ruleset workspaceID?: WorkspaceID cronRunID?: import("../cron/schema").CronRunID + metadata?: Record }) => Effect.Effect readonly fork: (input: { sessionID: SessionID; messageID?: MessageID }) => Effect.Effect readonly touch: (sessionID: SessionID) => Effect.Effect