Skip to content

Commit 599f6a2

Browse files
committed
Improved the get tasks tool, fixed mintlify MCP search
1 parent 03aec5a commit 599f6a2

File tree

9 files changed

+163
-35
lines changed

9 files changed

+163
-35
lines changed

apps/webapp/app/routes/api.v1.projects.$projectRef.$env.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,12 @@ export async function getEnvironmentFromEnv({
7474
projectId,
7575
userId,
7676
env,
77+
branch,
7778
}: {
7879
projectId: string;
7980
userId: string;
8081
env: ParamsSchema["env"];
82+
branch?: string;
8183
}): Promise<
8284
| {
8385
success: true;
@@ -126,6 +128,48 @@ export async function getEnvironmentFromEnv({
126128
break;
127129
}
128130

131+
if (slug === "preview") {
132+
if (!branch) {
133+
return {
134+
success: false,
135+
error: "Preview environment requires a branch. Please set the x-trigger-branch header.",
136+
};
137+
}
138+
139+
const previewEnvironment = await prisma.runtimeEnvironment.findFirst({
140+
where: {
141+
projectId,
142+
slug: "preview",
143+
},
144+
});
145+
146+
if (!previewEnvironment) {
147+
return {
148+
success: false,
149+
error: "Preview environment not found",
150+
};
151+
}
152+
153+
const branchEnvironment = await prisma.runtimeEnvironment.findFirst({
154+
where: {
155+
parentEnvironmentId: previewEnvironment.id,
156+
branchName: branch,
157+
},
158+
});
159+
160+
if (!branchEnvironment) {
161+
return {
162+
success: false,
163+
error: `Preview branch ${branch} not found`,
164+
};
165+
}
166+
167+
return {
168+
success: true,
169+
environment: branchEnvironment,
170+
};
171+
}
172+
129173
const environment = await prisma.runtimeEnvironment.findFirst({
130174
where: {
131175
projectId,

apps/webapp/app/routes/api.v1.projects.$projectRef.$env.workers.$tagName.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@ import { authenticateApiRequestWithPersonalAccessToken } from "~/services/person
55
import { findCurrentWorkerFromEnvironment } from "~/v3/models/workerDeployment.server";
66
import { getEnvironmentFromEnv } from "./api.v1.projects.$projectRef.$env";
77
import { GetWorkerByTagResponse } from "@trigger.dev/core/v3/schemas";
8+
import { env as $env } from "~/env.server";
9+
import { v3RunsPath } from "~/utils/pathBuilder";
810

911
const ParamsSchema = z.object({
1012
projectRef: z.string(),
1113
tagName: z.string(),
1214
env: z.enum(["dev", "staging", "prod", "preview"]),
1315
});
1416

17+
const HeadersSchema = z.object({
18+
"x-trigger-branch": z.string().optional(),
19+
});
20+
1521
type ParamsSchema = z.infer<typeof ParamsSchema>;
1622

1723
export async function loader({ request, params }: LoaderFunctionArgs) {
@@ -27,6 +33,10 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
2733
return json({ error: "Invalid Params" }, { status: 400 });
2834
}
2935

36+
const parsedHeaders = HeadersSchema.safeParse(Object.fromEntries(request.headers));
37+
38+
const branch = parsedHeaders.success ? parsedHeaders.data["x-trigger-branch"] : undefined;
39+
3040
const { projectRef, env } = parsedParams.data;
3141

3242
const project = await prisma.project.findFirst({
@@ -40,6 +50,15 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
4050
},
4151
},
4252
},
53+
select: {
54+
id: true,
55+
slug: true,
56+
organization: {
57+
select: {
58+
slug: true,
59+
},
60+
},
61+
},
4362
});
4463

4564
if (!project) {
@@ -50,6 +69,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
5069
projectId: project.id,
5170
userId: authenticationResult.userId,
5271
env,
72+
branch,
5373
});
5474

5575
if (!envResult.success) {
@@ -88,6 +108,15 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
88108
},
89109
});
90110

111+
const urls = {
112+
runs: `${$env.APP_ORIGIN}${v3RunsPath(
113+
{ slug: project.organization.slug },
114+
{ slug: project.slug },
115+
{ slug: env },
116+
{ versions: [currentWorker.version] }
117+
)}`,
118+
};
119+
91120
// Prepare the response object
92121
const response: GetWorkerByTagResponse = {
93122
worker: {
@@ -105,10 +134,8 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
105134
payloadSchema: task.payloadSchema,
106135
})),
107136
},
137+
urls,
108138
};
109139

110-
// Optionally validate the response before returning (for type safety)
111-
// WorkerResponseSchema.parse(response);
112-
113140
return json(response);
114141
}

packages/cli-v3/src/mcp/config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ export const toolsMetadata = {
4646
description:
4747
"Get all tasks in the project. Useful when searching for a task and for looking up a task identifier/slug",
4848
},
49+
get_current_worker: {
50+
name: "get_current_worker",
51+
title: "Get Current Worker",
52+
description:
53+
"Get the current worker for the project. Useful when searching for a task and for looking up a task identifier/slug and payload schema, or looking for the latest version in a specific environment.",
54+
},
4955
trigger_task: {
5056
name: "trigger_task",
5157
title: "Trigger Task",

packages/cli-v3/src/mcp/mintlifyClient.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
export async function performSearch(query: string) {
2-
const body = callToolBody("search", { query });
1+
export async function performSearch(query: string, signal: AbortSignal) {
2+
const body = callToolBody("Search", { query });
33

44
const response = await fetch("https://trigger.dev/docs/mcp", {
55
method: "POST",
@@ -8,6 +8,7 @@ export async function performSearch(query: string) {
88
Accept: "application/json, text/event-stream",
99
"MCP-Protocol-Version": "2025-06-18",
1010
},
11+
signal,
1112
body: JSON.stringify(body),
1213
});
1314

packages/cli-v3/src/mcp/schemas.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,10 @@ export const TriggerTaskInput = CommonProjectsInput.extend({
6767
"The ID/slug of the task to trigger. Use the get_tasks tool to get a list of tasks and ask the user to select one if it's not clear which one to use."
6868
),
6969
payload: z
70-
.string()
71-
.transform((val, ctx) => {
72-
try {
73-
return JSON.parse(val);
74-
} catch {
75-
ctx.addIssue({
76-
code: z.ZodIssueCode.custom,
77-
message: "The payload must be a valid JSON string",
78-
});
79-
return z.NEVER;
80-
}
81-
})
82-
.describe("The payload to trigger the task with, must be a valid JSON string"),
70+
.any()
71+
.describe(
72+
"The payload to trigger the task with. Should match the task's payload schema. Not a JSON string, but the actual payload object"
73+
),
8374
options: z
8475
.object({
8576
queue: z

packages/cli-v3/src/mcp/tools.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from "./tools/orgs.js";
1010
import { listPreviewBranchesTool } from "./tools/previewBranches.js";
1111
import { cancelRunTool, getRunDetailsTool, listRunsTool } from "./tools/runs.js";
12-
import { getTasksTool, triggerTaskTool } from "./tools/tasks.js";
12+
import { getCurrentWorker, triggerTaskTool } from "./tools/tasks.js";
1313
import { respondWithError } from "./utils.js";
1414

1515
export function registerTools(context: McpContext) {
@@ -19,7 +19,7 @@ export function registerTools(context: McpContext) {
1919
listProjectsTool,
2020
createProjectInOrgTool,
2121
initializeProjectTool,
22-
getTasksTool,
22+
getCurrentWorker,
2323
triggerTaskTool,
2424
listRunsTool,
2525
getRunDetailsTool,

packages/cli-v3/src/mcp/tools/docs.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ export const searchDocsTool = {
1010
inputSchema: {
1111
query: z.string(),
1212
},
13-
handler: toolHandler({ query: z.string() }, async (input, { ctx }) => {
13+
handler: toolHandler({ query: z.string() }, async (input, { ctx, signal }) => {
1414
ctx.logger?.log("calling search_docs", { input });
1515

16-
const results = await performSearch(input.query);
16+
const results = await performSearch(input.query, signal);
1717

18-
return {
19-
content: [{ type: "text", text: results.result }],
20-
};
18+
return results.result;
2119
}),
2220
};

packages/cli-v3/src/mcp/tools/tasks.ts

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { CommonProjectsInput, TriggerTaskInput } from "../schemas.js";
33
import { ToolMeta } from "../types.js";
44
import { respondWithError, toolHandler } from "../utils.js";
55

6-
export const getTasksTool = {
7-
name: toolsMetadata.get_tasks.name,
8-
title: toolsMetadata.get_tasks.title,
9-
description: toolsMetadata.get_tasks.description,
6+
export const getCurrentWorker = {
7+
name: toolsMetadata.get_current_worker.name,
8+
title: toolsMetadata.get_current_worker.title,
9+
description: toolsMetadata.get_current_worker.description,
1010
inputSchema: CommonProjectsInput.shape,
1111
handler: toolHandler(CommonProjectsInput.shape, async (input, { ctx }) => {
12-
ctx.logger?.log("calling get_tasks", { input });
12+
ctx.logger?.log("calling get_current_worker", { input });
1313

1414
if (ctx.options.devOnly && input.environment !== "dev") {
1515
return respondWithError(
@@ -24,14 +24,60 @@ export const getTasksTool = {
2424

2525
const cliApiClient = await ctx.getCliApiClient(input.branch);
2626

27-
const worker = await cliApiClient.getWorkerByTag(projectRef, input.environment, "current");
27+
const workerResult = await cliApiClient.getWorkerByTag(
28+
projectRef,
29+
input.environment,
30+
"current"
31+
);
32+
33+
if (!workerResult.success) {
34+
return respondWithError(workerResult.error);
35+
}
36+
37+
const { worker, urls } = workerResult.data;
38+
39+
const contents = [
40+
`Current worker for ${input.environment} is ${worker.version} using ${worker.sdkVersion} of the SDK.`,
41+
];
42+
43+
if (worker.tasks.length > 0) {
44+
contents.push(`The worker has ${worker.tasks.length} tasks registered:`);
45+
46+
for (const task of worker.tasks) {
47+
if (task.payloadSchema) {
48+
contents.push(
49+
`- ${task.slug} in ${task.filePath} (payload schema: ${JSON.stringify(
50+
task.payloadSchema
51+
)})`
52+
);
53+
} else {
54+
contents.push(`- ${task.slug} in ${task.filePath}`);
55+
}
56+
}
57+
} else {
58+
contents.push(`The worker has no tasks registered.`);
59+
}
2860

29-
if (!worker.success) {
30-
return respondWithError(worker.error);
61+
contents.push(`\n`);
62+
contents.push(`URLs:`);
63+
contents.push(`- Runs: ${urls.runs}`);
64+
contents.push(`\n`);
65+
contents.push(
66+
`You can use the list_runs tool with the version ${worker.version} to get the list of runs for this worker.`
67+
);
68+
69+
if (
70+
typeof worker.sdkVersion === "string" &&
71+
typeof worker.cliVersion === "string" &&
72+
worker.sdkVersion !== worker.cliVersion
73+
) {
74+
contents.push(
75+
`WARNING: The SDK version (${worker.sdkVersion}) is different from the CLI version (${worker.cliVersion}). This might cause issues with the task execution. Make sure to pin the CLI and the SDK versions to ${worker.sdkVersion}.`
76+
);
3177
}
3278

3379
return {
34-
content: [{ type: "text", text: JSON.stringify(worker.data, null, 2) }],
80+
content: [{ type: "text", text: contents.join("\n") }],
3581
};
3682
}),
3783
};
@@ -62,8 +108,20 @@ export const triggerTaskTool = {
62108
branch: input.branch,
63109
});
64110

111+
ctx.logger?.log("triggering task", { input });
112+
113+
let payload = input.payload;
114+
115+
if (typeof payload === "string") {
116+
try {
117+
payload = JSON.parse(payload);
118+
} catch {
119+
ctx.logger?.log("payload is not a valid JSON string, using as is", { payload });
120+
}
121+
}
122+
65123
const result = await apiClient.triggerTask(input.taskId, {
66-
payload: input.payload,
124+
payload,
67125
options: input.options,
68126
});
69127

packages/core/src/v3/schemas/api.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ export const GetWorkerByTagResponse = z.object({
100100
cliVersion: z.string().nullish(),
101101
tasks: z.array(GetWorkerTaskResponse),
102102
}),
103+
urls: z.object({
104+
runs: z.string(),
105+
}),
103106
});
104107

105108
export type GetWorkerByTagResponse = z.infer<typeof GetWorkerByTagResponse>;

0 commit comments

Comments
 (0)