fix(cli): various mcp server fixes (#2430)
* fix: span events are optional, as logs don't have them * remove CI: true env var to prevent massive tool responses * fix github resolver relative path * prevent the dev command from asking for user input when not interactive * Remove lodash.get because it's deprecated * span output can be object other than an object, like a string * prevent large traces from causing get_run_details failures * Decreased max log lines, made it a tool input property * Some install mcp tweaks
This commit is contained in:
@@ -436,6 +436,7 @@ const EnvironmentSchema = z.object({
|
||||
EVENT_LOOP_MONITOR_ENABLED: z.string().default("1"),
|
||||
MAXIMUM_LIVE_RELOADING_EVENTS: z.coerce.number().int().default(1000),
|
||||
MAXIMUM_TRACE_SUMMARY_VIEW_COUNT: z.coerce.number().int().default(25_000),
|
||||
MAXIMUM_TRACE_DETAILED_SUMMARY_VIEW_COUNT: z.coerce.number().int().default(10_000),
|
||||
TASK_PAYLOAD_OFFLOAD_THRESHOLD: z.coerce.number().int().default(524_288), // 512KB
|
||||
TASK_PAYLOAD_MAXIMUM_SIZE: z.coerce.number().int().default(3_145_728), // 3MB
|
||||
BATCH_TASK_PAYLOAD_MAXIMUM_SIZE: z.coerce.number().int().default(1_000_000), // 1MB
|
||||
|
||||
@@ -285,7 +285,7 @@ export class TaskEventStore {
|
||||
: Prisma.empty
|
||||
}
|
||||
ORDER BY "startTime" ASC
|
||||
LIMIT ${env.MAXIMUM_TRACE_SUMMARY_VIEW_COUNT}
|
||||
LIMIT ${env.MAXIMUM_TRACE_DETAILED_SUMMARY_VIEW_COUNT}
|
||||
`;
|
||||
} else {
|
||||
return await this.readReplica.$queryRaw<DetailedTraceEvent[]>`
|
||||
@@ -320,7 +320,7 @@ export class TaskEventStore {
|
||||
: Prisma.empty
|
||||
}
|
||||
ORDER BY "startTime" ASC
|
||||
LIMIT ${env.MAXIMUM_TRACE_SUMMARY_VIEW_COUNT}
|
||||
LIMIT ${env.MAXIMUM_TRACE_DETAILED_SUMMARY_VIEW_COUNT}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,47 +107,50 @@ export function configureDevCommand(program: Command) {
|
||||
export async function devCommand(options: DevCommandOptions) {
|
||||
runtimeChecks();
|
||||
|
||||
const skipMCPInstall = typeof options.skipMCPInstall === "boolean" && options.skipMCPInstall;
|
||||
// Only show these install prompts if the user is in a terminal (not in a Coding Agent)
|
||||
if (process.stdout.isTTY) {
|
||||
const skipMCPInstall = typeof options.skipMCPInstall === "boolean" && options.skipMCPInstall;
|
||||
|
||||
if (!skipMCPInstall) {
|
||||
const hasSeenMCPInstallPrompt = readConfigHasSeenMCPInstallPrompt();
|
||||
if (!skipMCPInstall) {
|
||||
const hasSeenMCPInstallPrompt = readConfigHasSeenMCPInstallPrompt();
|
||||
|
||||
if (!hasSeenMCPInstallPrompt) {
|
||||
const installChoice = await confirm({
|
||||
message: "Would you like to install the Trigger.dev MCP server?",
|
||||
initialValue: true,
|
||||
});
|
||||
if (!hasSeenMCPInstallPrompt) {
|
||||
const installChoice = await confirm({
|
||||
message: "Would you like to install the Trigger.dev MCP server?",
|
||||
initialValue: true,
|
||||
});
|
||||
|
||||
writeConfigHasSeenMCPInstallPrompt(true);
|
||||
writeConfigHasSeenMCPInstallPrompt(true);
|
||||
|
||||
const skipInstall = isCancel(installChoice) || !installChoice;
|
||||
const skipInstall = isCancel(installChoice) || !installChoice;
|
||||
|
||||
if (!skipInstall) {
|
||||
log.step("Welcome to the Trigger.dev MCP server install wizard 🧙");
|
||||
if (!skipInstall) {
|
||||
log.step("Welcome to the Trigger.dev MCP server install wizard 🧙");
|
||||
|
||||
const [installError] = await tryCatch(
|
||||
installMcpServer({
|
||||
yolo: false,
|
||||
tag: VERSION as string,
|
||||
logLevel: options.logLevel,
|
||||
})
|
||||
);
|
||||
const [installError] = await tryCatch(
|
||||
installMcpServer({
|
||||
yolo: false,
|
||||
tag: VERSION as string,
|
||||
logLevel: options.logLevel,
|
||||
})
|
||||
);
|
||||
|
||||
if (installError) {
|
||||
log.error(`Failed to install MCP server: ${installError.message}`);
|
||||
if (installError) {
|
||||
log.error(`Failed to install MCP server: ${installError.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const skipRulesInstall =
|
||||
typeof options.skipRulesInstall === "boolean" && options.skipRulesInstall;
|
||||
const skipRulesInstall =
|
||||
typeof options.skipRulesInstall === "boolean" && options.skipRulesInstall;
|
||||
|
||||
if (!skipRulesInstall) {
|
||||
await initiateRulesInstallWizard({
|
||||
manifestPath: options.rulesInstallManifestPath,
|
||||
branch: options.rulesInstallBranch,
|
||||
});
|
||||
if (!skipRulesInstall) {
|
||||
await initiateRulesInstallWizard({
|
||||
manifestPath: options.rulesInstallManifestPath,
|
||||
branch: options.rulesInstallBranch,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const authorization = await login({
|
||||
|
||||
@@ -233,12 +233,12 @@ export async function installMcpServer(
|
||||
);
|
||||
|
||||
log.info("More examples:");
|
||||
log.message(` • ${chalk.green('"List my Trigger.dev projects"')}`);
|
||||
log.message(` • ${chalk.green('"Create a new Trigger.dev project called MyApp"')}`);
|
||||
log.message(` • ${chalk.green('"Show me all tasks in my project"')}`);
|
||||
log.message(` • ${chalk.green('"Trigger the email-notification task"')}`);
|
||||
log.message(` • ${chalk.green('"Trigger the hello-world task"')}`);
|
||||
log.message(` • ${chalk.green('"Can you help me debug the prod run run_1234"')}`);
|
||||
log.message(` • ${chalk.green('"Deploy my trigger project to staging"')}`);
|
||||
log.message(` • ${chalk.green('"What trigger task handles uploading files to S3?"')}`);
|
||||
log.message(` • ${chalk.green('"How do I create a scheduled task in Trigger.dev?"')}`);
|
||||
log.message(` • ${chalk.green('"Search Trigger.dev docs for webhook examples"')}`);
|
||||
log.message(` • ${chalk.green('"Search Trigger.dev docs for ffmpeg examples"')}`);
|
||||
|
||||
log.info("Helpful links:");
|
||||
log.message(` • ${cliLink("Trigger.dev docs", "https://trigger.dev/docs")}`);
|
||||
@@ -318,17 +318,13 @@ async function installMcpServerForClient(
|
||||
return;
|
||||
}
|
||||
|
||||
const clientSpinner = spinner();
|
||||
|
||||
clientSpinner.start(`Installing in ${clientName}`);
|
||||
|
||||
const scope = await resolveScopeForClient(clientName, options);
|
||||
|
||||
clientSpinner.message(`Installing in ${scope.scope} scope at ${scope.location}`);
|
||||
// clientSpinner.message(`Installing in ${scope.scope} scope at ${scope.location}`);
|
||||
|
||||
const configPath = await performInstallForClient(clientName, scope, options);
|
||||
|
||||
clientSpinner.stop(`Successfully installed in ${clientName} (${configPath})`);
|
||||
// clientSpinner.stop(`Successfully installed in ${clientName} (${configPath})`);
|
||||
|
||||
return { configPath, clientName, scope };
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
} from "@trigger.dev/core/v3/schemas";
|
||||
import type { CursorPageResponse } from "@trigger.dev/core/v3/zodfetch";
|
||||
|
||||
const DEFAULT_MAX_TRACE_LINES = 500;
|
||||
|
||||
export function formatRun(run: RetrieveRunResponse): string {
|
||||
const lines: string[] = [];
|
||||
|
||||
@@ -170,14 +172,21 @@ function formatRelatedRuns(relatedRuns: RetrieveRunResponse["relatedRuns"]): str
|
||||
return parts.length > 0 ? `Related: ${parts.join("; ")}` : null;
|
||||
}
|
||||
|
||||
export function formatRunTrace(trace: RetrieveRunTraceResponseBody["trace"]): string {
|
||||
export function formatRunTrace(
|
||||
trace: RetrieveRunTraceResponseBody["trace"],
|
||||
maxTraceLines: number = DEFAULT_MAX_TRACE_LINES
|
||||
): string {
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push(`Trace ID: ${trace.traceId}`);
|
||||
lines.push("");
|
||||
|
||||
// Format the root span and its children recursively
|
||||
formatSpan(trace.rootSpan, lines, 0);
|
||||
const reachedMaxLines = formatSpan(trace.rootSpan, lines, 0, maxTraceLines);
|
||||
|
||||
if (reachedMaxLines) {
|
||||
lines.push(`(truncated logs to ${maxTraceLines} lines)`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
@@ -185,8 +194,13 @@ export function formatRunTrace(trace: RetrieveRunTraceResponseBody["trace"]): st
|
||||
function formatSpan(
|
||||
span: RetrieveRunTraceResponseBody["trace"]["rootSpan"],
|
||||
lines: string[],
|
||||
depth: number
|
||||
): void {
|
||||
depth: number,
|
||||
maxLines: number
|
||||
): boolean {
|
||||
if (lines.length >= maxLines) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const indent = " ".repeat(depth);
|
||||
const prefix = depth === 0 ? "└─" : "├─";
|
||||
|
||||
@@ -230,7 +244,7 @@ function formatSpan(
|
||||
}
|
||||
|
||||
// Show output if it exists
|
||||
if (span.data.output && Object.keys(span.data.output).length > 0) {
|
||||
if (span.data.output) {
|
||||
lines.push(
|
||||
`${indent} Output: ${JSON.stringify(span.data.output, null, 2).replace(
|
||||
/\n/g,
|
||||
@@ -263,14 +277,20 @@ function formatSpan(
|
||||
|
||||
// Recursively format children
|
||||
if (span.children) {
|
||||
span.children.forEach((child, index) => {
|
||||
formatSpan(child, lines, depth + 1);
|
||||
const reachedMaxLines = span.children.some((child, index) => {
|
||||
const reachedMaxLines = formatSpan(child, lines, depth + 1, maxLines);
|
||||
// Add spacing between sibling spans (except for the last one)
|
||||
if (index < span.children.length - 1) {
|
||||
if (index < span.children.length - 1 && !reachedMaxLines) {
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
return reachedMaxLines;
|
||||
});
|
||||
|
||||
return reachedMaxLines;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getStatusIndicator(
|
||||
|
||||
@@ -123,7 +123,13 @@ export const CommonRunsInput = CommonProjectsInput.extend({
|
||||
|
||||
export type CommonRunsInput = z.output<typeof CommonRunsInput>;
|
||||
|
||||
export const GetRunDetailsInput = CommonRunsInput.extend({});
|
||||
export const GetRunDetailsInput = CommonRunsInput.extend({
|
||||
maxTraceLines: z
|
||||
.number()
|
||||
.int()
|
||||
.describe("The maximum number of lines to show in the trace. Defaults to 500")
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type GetRunDetailsInput = z.output<typeof GetRunDetailsInput>;
|
||||
|
||||
|
||||
@@ -74,7 +74,6 @@ export const deployTool = {
|
||||
cwd: cwd.cwd,
|
||||
env: {
|
||||
TRIGGER_MCP_SERVER: "1",
|
||||
CI: "true",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ export const getRunDetailsTool = {
|
||||
]);
|
||||
|
||||
const formattedRun = formatRun(runResult);
|
||||
const formattedTrace = formatRunTrace(traceResult.trace);
|
||||
const formattedTrace = formatRunTrace(traceResult.trace, input.maxTraceLines);
|
||||
|
||||
const runUrl = await ctx.getDashboardUrl(`/projects/v3/${projectRef}/runs/${runResult.id}`);
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ export class GithubRulesManifestLoader implements RulesManifestLoader {
|
||||
|
||||
async loadRulesFile(relativePath: string): Promise<string> {
|
||||
const response = await fetch(
|
||||
`https://raw.githubusercontent.com/triggerdotdev/trigger.dev/refs/heads/${this.branch}/${relativePath}`
|
||||
`https://raw.githubusercontent.com/triggerdotdev/trigger.dev/refs/heads/${this.branch}/rules/${relativePath}`
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
@@ -187,7 +187,6 @@
|
||||
"execa": "^8.0.1",
|
||||
"humanize-duration": "^3.27.3",
|
||||
"jose": "^5.4.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"nanoid": "3.3.8",
|
||||
"prom-client": "^15.1.0",
|
||||
"socket.io": "4.7.4",
|
||||
|
||||
@@ -1233,7 +1233,7 @@ export const RetrieveRunTraceSpanSchema = z.object({
|
||||
runId: z.string(),
|
||||
taskSlug: z.string().optional(),
|
||||
taskPath: z.string().optional(),
|
||||
events: z.array(z.any()),
|
||||
events: z.array(z.any()).optional(),
|
||||
startTime: z.coerce.date(),
|
||||
duration: z.number(),
|
||||
isError: z.boolean(),
|
||||
@@ -1245,7 +1245,7 @@ export const RetrieveRunTraceSpanSchema = z.object({
|
||||
queueName: z.string().optional(),
|
||||
machinePreset: z.string().optional(),
|
||||
properties: z.record(z.any()).optional(),
|
||||
output: z.record(z.any()).optional(),
|
||||
output: z.unknown().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { SemanticInternalAttributes } from "../semanticInternalAttributes.js";
|
||||
import { TriggerTracer } from "../tracer.js";
|
||||
import { zodfetch } from "../zodfetch.js";
|
||||
import { flattenAttributes } from "./flattenAttributes.js";
|
||||
import get from "lodash.get";
|
||||
import { JSONHeroPath } from "@jsonhero/path";
|
||||
|
||||
export type IOPacket = {
|
||||
data?: string | undefined;
|
||||
@@ -536,7 +536,7 @@ export async function replaceSuperJsonPayload(original: string, newPayload: stri
|
||||
.map(([key]) => key);
|
||||
|
||||
const overridenUndefinedKeys = originalUndefinedKeys.filter(
|
||||
(key) => get(newPayloadObject, key) !== undefined
|
||||
(key) => getKeyFromObject(newPayloadObject, key) !== undefined
|
||||
);
|
||||
|
||||
overridenUndefinedKeys.forEach((key) => {
|
||||
@@ -551,3 +551,9 @@ export async function replaceSuperJsonPayload(original: string, newPayload: stri
|
||||
|
||||
return superjson.deserialize(newSuperJson);
|
||||
}
|
||||
|
||||
function getKeyFromObject(object: unknown, key: string) {
|
||||
const jsonHeroPath = new JSONHeroPath(key);
|
||||
|
||||
return jsonHeroPath.first(object);
|
||||
}
|
||||
|
||||
Generated
-8
@@ -1576,9 +1576,6 @@ importers:
|
||||
jose:
|
||||
specifier: ^5.4.0
|
||||
version: 5.4.0
|
||||
lodash.get:
|
||||
specifier: ^4.4.2
|
||||
version: 4.4.2
|
||||
nanoid:
|
||||
specifier: 3.3.8
|
||||
version: 3.3.8
|
||||
@@ -28730,11 +28727,6 @@ packages:
|
||||
resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
|
||||
dev: true
|
||||
|
||||
/lodash.get@4.4.2:
|
||||
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
|
||||
deprecated: This package is deprecated. Use the optional chaining (?.) operator instead.
|
||||
dev: false
|
||||
|
||||
/lodash.groupby@4.6.0:
|
||||
resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==}
|
||||
dev: false
|
||||
|
||||
Reference in New Issue
Block a user