diff --git a/.changeset/remove-dev-mcp-options.md b/.changeset/remove-dev-mcp-options.md new file mode 100644 index 00000000000..f4b400b3092 --- /dev/null +++ b/.changeset/remove-dev-mcp-options.md @@ -0,0 +1,5 @@ +--- +"trigger.dev": patch +--- + +Remove the legacy `--mcp` and `--mcp-port` options from the `dev` command. Run the dedicated `trigger mcp` command to start the Trigger.dev MCP server. diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index 78dfd9cbecc..c0ccb5ac777 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -56,7 +56,6 @@ "@types/gradient-string": "^1.1.2", "@types/ini": "^4.1.1", "@types/object-hash": "3.0.6", - "@types/polka": "^0.5.7", "@types/react": "^18.2.48", "@types/resolve": "^1.20.6", "@types/rimraf": "^4.0.5", @@ -133,7 +132,6 @@ "p-retry": "^6.1.0", "partysocket": "^1.0.2", "pkg-types": "^1.1.3", - "polka": "^0.5.2", "resolve": "^1.22.8", "semver": "^7.5.0", "signal-exit": "^4.1.0", diff --git a/packages/cli-v3/src/commands/dev.ts b/packages/cli-v3/src/commands/dev.ts index 86973d628ae..9daf07532ce 100644 --- a/packages/cli-v3/src/commands/dev.ts +++ b/packages/cli-v3/src/commands/dev.ts @@ -59,8 +59,6 @@ const DevCommandOptions = CommonCommandOptions.extend({ envFile: z.string().optional(), keepTmpFiles: z.boolean().default(false), maxConcurrentRuns: z.coerce.number().optional(), - mcp: z.boolean().default(false), - mcpPort: z.coerce.number().optional().default(3333), analyze: z.boolean().default(false), disableWarnings: z.boolean().default(false), skipMCPInstall: z.boolean().default(false), @@ -101,8 +99,6 @@ export function configureDevCommand(program: Command) { "--keep-tmp-files", "Keep temporary files after the dev session ends, helpful for debugging" ) - .option("--mcp", "Start the MCP server") - .option("--mcp-port", "The port to run the MCP server on", "3333") .addOption( new CommandOption("--analyze", "Analyze the build output and import timings").hideHelp() ) diff --git a/packages/cli-v3/src/dev/devSession.ts b/packages/cli-v3/src/dev/devSession.ts index c285624823e..24259c0c119 100644 --- a/packages/cli-v3/src/dev/devSession.ts +++ b/packages/cli-v3/src/dev/devSession.ts @@ -23,7 +23,6 @@ import type { EphemeralDirectory } from "../utilities/tempDirectories.js"; import { clearTmpDirs, getStoreDir, getTmpDir } from "../utilities/tempDirectories.js"; import { startDevOutput } from "./devOutput.js"; import { startWorkerRuntime } from "./devSupervisor.js"; -import { startMcpServer, stopMcpServer } from "./mcpServer.js"; import { writeJSONFile } from "../utilities/fileSystem.js"; import { join } from "node:path"; @@ -67,17 +66,6 @@ export async function startDevSession({ dashboardUrl, }); - if (rawArgs.mcp) { - await startMcpServer({ - port: rawArgs.mcpPort, - cliApiClient: client, - devSession: { - dashboardUrl, - projectRef: rawConfig.project, - }, - }); - } - const stopOutput = startDevOutput({ name, branch, @@ -237,7 +225,6 @@ export async function startDevSession({ stopBundling?.().catch((error) => {}); runtime.shutdown().catch((error) => {}); stopOutput(); - stopMcpServer(); }, }; } diff --git a/packages/cli-v3/src/dev/mcpServer.ts b/packages/cli-v3/src/dev/mcpServer.ts deleted file mode 100644 index 8d0d49efeca..00000000000 --- a/packages/cli-v3/src/dev/mcpServer.ts +++ /dev/null @@ -1,266 +0,0 @@ -import polka from "polka"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; -import { z } from "zod"; -import { logger } from "../utilities/logger.js"; -import type { CliApiClient } from "../apiClient.js"; -import { ApiClient, RunStatus } from "@trigger.dev/core/v3"; -import { eventBus } from "../utilities/eventBus.js"; - -let allTaskIds: string[] = []; -let projectRef: string; -let dashboardUrl: string; -// there is some overlap between `ApiClient` and `CliApiClient` which is not ideal -// we can address in this in the future, but for now we need keep using both -// as `ApiClient` exposes most of the methods needed for the MCP tools -let sdkApiClient: ApiClient; - -let mcpTransport: SSEServerTransport | null = null; - -const server = new McpServer({ - name: "trigger.dev", - version: "1.0.0", -}); - -// The `list-all-tasks` tool primarily helps to enable fuzzy matching of task IDs (names). -// This way, one doesn't need to specify the full task ID and rather let the LLM figure it out. -// This could be a good fit for the `resource` entity in MCP. -// Also, a custom `prompt` entity could be useful to instruct the LLM to prompt the user -// for selecting a task from a list of matching tasks, when the confidence for an exact match is low. -server.tool("list-all-tasks", "List all available task IDs in the worker.", async () => { - return { - content: [ - { - text: JSON.stringify(allTaskIds, null, 2), - type: "text", - }, - ], - }; -}); - -server.tool( - "trigger-task", - "Trigger a task", - { - id: z.string().describe("The ID of the task to trigger"), - payload: z - .string() - .transform((val, ctx) => { - try { - return JSON.parse(val); - } catch { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "The payload must be a valid JSON string", - }); - return z.NEVER; - } - }) - .describe("The payload to pass to the task run, must be a valid JSON"), - // TODO: expose more parameteres from the trigger options - }, - async ({ id, payload }) => { - const result = await sdkApiClient.triggerTask(id, { - payload, - }); - - const taskRunUrl = `${dashboardUrl}/projects/v3/${projectRef}/runs/${result.id}`; - - return { - content: [ - { - type: "text", - text: JSON.stringify({ ...result, taskRunUrl }, null, 2), - }, - ], - }; - } -); - -server.tool( - "list-runs", - "List task runs. This returns a paginated list which shows the details of the runs, e.g., status, attempts, cost, etc.", - { - filters: z - .object({ - status: RunStatus.optional().describe( - "The status of the run. Can be WAITING_FOR_DEPLOY, QUEUED, EXECUTING, REATTEMPTING, or FROZEN" - ), - taskIdentifier: z - .union([z.array(z.string()), z.string()]) - .optional() - .describe("The identifier of the task that was run"), - version: z - .union([z.array(z.string()), z.string()]) - .optional() - .describe("The version of the worker that executed the run"), - from: z - .union([z.date(), z.number()]) - .optional() - .describe("Start date/time for filtering runs"), - to: z.union([z.date(), z.number()]).optional().describe("End date/time for filtering runs"), - period: z.string().optional().describe("Time period for filtering runs"), - bulkAction: z - .string() - .optional() - .describe("The bulk action ID to filter the runs by (e.g., bulk_1234)"), - tag: z - .union([z.array(z.string()), z.string()]) - .optional() - .describe("The tags that are attached to the run"), - schedule: z - .string() - .optional() - .describe("The schedule ID to filter the runs by (e.g., schedule_1234)"), - isTest: z.boolean().optional().describe("Whether the run is a test run or not"), - batch: z.string().optional().describe("The batch identifier to filter runs by"), - }) - .describe("Parameters for listing task runs"), - }, - async ({ filters }) => { - const { data, pagination } = await sdkApiClient.listRuns(filters); - - return { - content: [ - { - type: "text", - text: JSON.stringify({ data, pagination }, null, 2), - }, - ], - }; - } -); - -server.tool( - "get-run", - "Retrieve the details of a task run, e.g., status, attempts, cost, etc.", - { - runId: z.string().describe("The ID of the task run to get"), - }, - async ({ runId }) => { - const result = await sdkApiClient.retrieveRun(runId); - - return { - content: [ - { - type: "text", - text: JSON.stringify(result, null, 2), - }, - ], - }; - } -); - -server.tool( - "cancel-run", - "Cancel an in-progress run. Runs that have already completed cannot be cancelled.", - { - runId: z.string().describe("The ID of the task run to cancel"), - }, - async ({ runId }) => { - const run = await sdkApiClient.retrieveRun(runId); - - if (run?.status === "COMPLETED") { - return { - content: [ - { - type: "text", - text: JSON.stringify( - { message: "This run is already completed, no action taken.", run }, - null, - 2 - ), - }, - ], - }; - } - - await sdkApiClient.cancelRun(runId); - // we could also skip fetching the run again, but it provides more context to the LLM - // and one extra API call is no big deal - const updatedRun = await sdkApiClient.retrieveRun(runId); - - return { - content: [ - { - type: "text", - text: JSON.stringify( - { - message: "Task run was cancelled", - previousStatus: run.status, - currentStatus: updatedRun.status, - updatedTaskRun: updatedRun, - }, - null, - 2 - ), - }, - ], - }; - } -); - -server.tool( - "get-run-logs", - "Retrieve the logs output of a task run.", - { - runId: z.string().describe("The ID of the task run to get"), - }, - async ({ runId }) => { - const result = await sdkApiClient.listRunEvents(runId); - - return { - content: [ - { - text: JSON.stringify(result, null, 2), - type: "text", - }, - ], - }; - } -); - -const app = polka(); -app.get("/sse", (_req, res) => { - mcpTransport = new SSEServerTransport("/messages", res); - server.connect(mcpTransport); -}); -app.post("/messages", (req, res) => { - if (mcpTransport) { - mcpTransport.handlePostMessage(req, res); - } -}); - -eventBus.on("backgroundWorkerInitialized", (worker) => { - allTaskIds = worker.manifest?.tasks.map((task) => task.id) ?? []; -}); - -export const startMcpServer = async (options: { - port: number; - cliApiClient: CliApiClient; - devSession: { - dashboardUrl: string; - projectRef: string; - }; -}) => { - const { apiURL, accessToken } = options.cliApiClient; - - if (!accessToken) { - logger.error("No access token found in the API client, failed to start the MCP server"); - return; - } - - sdkApiClient = new ApiClient(apiURL, accessToken); - projectRef = options.devSession.projectRef; - dashboardUrl = options.devSession.dashboardUrl; - - app.listen(options.port, () => { - logger.info(`Trigger.dev MCP Server is now running on port ${options.port} ✨`); - }); -}; - -export const stopMcpServer = () => { - app.server?.close(() => { - logger.info(`Trigger.dev MCP Server is now stopped`); - }); -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 851d7544ae9..7e553d0d24a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1576,9 +1576,6 @@ importers: pkg-types: specifier: ^1.1.3 version: 1.1.3 - polka: - specifier: ^0.5.2 - version: 0.5.2 resolve: specifier: ^1.22.8 version: 1.22.8 @@ -1643,9 +1640,6 @@ importers: '@types/object-hash': specifier: 3.0.6 version: 3.0.6 - '@types/polka': - specifier: ^0.5.7 - version: 0.5.7 '@types/react': specifier: ^18.2.48 version: 18.2.48 @@ -2292,10 +2286,6 @@ packages: '@ark/util@0.46.0': resolution: {integrity: sha512-JPy/NGWn/lvf1WmGCPw2VGpBg5utZraE84I7wli18EDF3p3zc/e9WolT35tINeZO3l7C77SjqRJeAUoT0CvMRg==} - '@arr/every@1.0.1': - resolution: {integrity: sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==} - engines: {node: '>=4'} - '@aws-crypto/crc32@5.2.0': resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} engines: {node: '>=16.0.0'} @@ -5539,9 +5529,6 @@ packages: engines: {node: '>=16'} hasBin: true - '@polka/url@0.5.0': - resolution: {integrity: sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==} - '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -7929,9 +7916,6 @@ packages: '@types/pg@8.6.1': resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} - '@types/polka@0.5.7': - resolution: {integrity: sha512-TH8CDXM8zoskPCNmWabtK7ziGv9Q21s4hMZLVYK5HFEfqmGXBqq/Wgi7jNELWXftZK/1J/9CezYa06x1RKeQ+g==} - '@types/prismjs@1.26.0': resolution: {integrity: sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==} @@ -8031,9 +8015,6 @@ packages: '@types/tinycolor2@1.4.3': resolution: {integrity: sha512-Kf1w9NE5HEgGxCRyIcRXR/ZYtDv0V8FVPtYHwLxl0O+maGX0erE77pQlD0gpP+/KByMZ87mOA79SjifhSB3PjQ==} - '@types/trouter@3.1.4': - resolution: {integrity: sha512-4YIL/2AvvZqKBWenjvEpxpblT2KGO6793ipr5QS7/6DpQ3O3SwZGgNGWezxf3pzeYZc24a2pJIrR/+Jxh/wYNQ==} - '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -11734,10 +11715,6 @@ packages: match-sorter@6.3.4: resolution: {integrity: sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==} - matchit@1.1.0: - resolution: {integrity: sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==} - engines: {node: '>=6'} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -12966,9 +12943,6 @@ packages: resolution: {integrity: sha512-OLS/0XeUAcE8a2fdwemNja+udKgXNnY6yKVIXqAD2zVRx1KvY6Ato/rZ2vdzbxqYwPW0u6SCNC/bAMPNzpzxbw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - polka@0.5.2: - resolution: {integrity: sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==} - possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -14657,10 +14631,6 @@ packages: trough@2.1.0: resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} - trouter@2.0.1: - resolution: {integrity: sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==} - engines: {node: '>=6'} - ts-dedent@2.2.0: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} @@ -15732,8 +15702,6 @@ snapshots: '@ark/util@0.46.0': {} - '@arr/every@1.0.1': {} - '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 @@ -19780,8 +19748,6 @@ snapshots: optionalDependencies: fsevents: 2.3.2 - '@polka/url@0.5.0': {} - '@popperjs/core@2.11.8': {} '@posthog/core@1.29.13': @@ -22651,13 +22617,6 @@ snapshots: pg-protocol: 1.10.3 pg-types: 2.2.0 - '@types/polka@0.5.7': - dependencies: - '@types/express': 4.17.15 - '@types/express-serve-static-core': 4.17.32 - '@types/node': 22.20.0 - '@types/trouter': 3.1.4 - '@types/prismjs@1.26.0': {} '@types/prop-types@15.7.5': {} @@ -22773,8 +22732,6 @@ snapshots: '@types/tinycolor2@1.4.3': {} - '@types/trouter@3.1.4': {} - '@types/trusted-types@2.0.7': optional: true @@ -26952,10 +26909,6 @@ snapshots: '@babel/runtime': 7.24.5 remove-accents: 0.5.0 - matchit@1.1.0: - dependencies: - '@arr/every': 1.0.1 - math-intrinsics@1.1.0: {} mdast-util-definitions@5.1.1: @@ -28645,11 +28598,6 @@ snapshots: polite-json@5.0.0: {} - polka@0.5.2: - dependencies: - '@polka/url': 0.5.0 - trouter: 2.0.1 - possible-typed-array-names@1.0.0: {} postcss-discard-duplicates@5.1.0(postcss@8.5.10): @@ -30655,10 +30603,6 @@ snapshots: trough@2.1.0: {} - trouter@2.0.1: - dependencies: - matchit: 1.1.0 - ts-dedent@2.2.0: {} ts-easing@0.2.0: {}