Skip to content

Commit 47b4e1f

Browse files
committed
Massive MCP cleanup
1 parent d13c4bf commit 47b4e1f

File tree

15 files changed

+1291
-1527
lines changed

15 files changed

+1291
-1527
lines changed

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

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,11 @@ import { CommonCommandOptions, commonOptions, wrapCommandAction } from "../cli/c
99
import { CLOUD_API_URL } from "../consts.js";
1010
import { McpContext } from "../mcp/context.js";
1111
import { FileLogger } from "../mcp/logger.js";
12-
import {
13-
registerCancelRunTool,
14-
registerCreateProjectTool,
15-
registerDeployTool,
16-
registerGetRunDetailsTool,
17-
registerGetTasksTool,
18-
registerInitializeProjectTool,
19-
registerListDeploymentsTool,
20-
registerListOrgsTool,
21-
registerListProjectsTool,
22-
registerListRunsTool,
23-
registerSearchDocsTool,
24-
registerTriggerTaskTool,
25-
registerListPreviewBranchesTool,
26-
} from "../mcp/tools.js";
12+
import { registerTools } from "../mcp/tools.js";
2713
import { printStandloneInitialBanner } from "../utilities/initialBanner.js";
2814
import { logger } from "../utilities/logger.js";
2915
import { installMcpServer } from "./install-mcp.js";
16+
import { serverMetadata } from "../mcp/config.js";
3017

3118
const McpCommandOptions = CommonCommandOptions.extend({
3219
projectRef: z.string().optional(),
@@ -79,10 +66,9 @@ export async function mcpCommand(options: McpCommandOptions) {
7966
logger.loggerLevel = "none";
8067

8168
const server = new McpServer({
82-
name: "triggerdev",
83-
version: "1.0.0",
84-
description:
85-
"Trigger.dev MCP server to automate your Trigger.dev projects and answer questions about Trigger.dev by searching the docs. If you need help setting up Trigger.dev in your project please refer to https://trigger.dev/docs/manual-setup. If the user asks for help with adding Trigger.dev to their project, please refer to https://trigger.dev/docs/manual-setup.",
69+
name: serverMetadata.name,
70+
version: serverMetadata.version,
71+
description: serverMetadata.instructions,
8672
});
8773

8874
server.server.oninitialized = async () => {
@@ -103,19 +89,7 @@ export async function mcpCommand(options: McpCommandOptions) {
10389
profile: options.profile,
10490
});
10591

106-
registerSearchDocsTool(context);
107-
registerInitializeProjectTool(context);
108-
registerGetTasksTool(context);
109-
registerTriggerTaskTool(context);
110-
registerGetRunDetailsTool(context);
111-
registerCancelRunTool(context);
112-
registerListRunsTool(context);
113-
registerListProjectsTool(context);
114-
registerListOrgsTool(context);
115-
registerCreateProjectTool(context);
116-
registerDeployTool(context);
117-
registerListDeploymentsTool(context);
118-
registerListPreviewBranchesTool(context);
92+
registerTools(context);
11993

12094
await server.connect(transport);
12195
}

packages/cli-v3/src/commands/update.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ async function getTriggerDependencies(
330330

331331
export async function tryResolveTriggerPackageVersion(
332332
name: string,
333-
basedir: string
333+
basedir?: string
334334
): Promise<string | undefined> {
335335
try {
336336
const resolvedPath = nodeResolve.sync(name, {

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { McpContext } from "./context.js";
1+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22

3-
export function hasRootsCapability(context: McpContext) {
4-
const capabilities = context.server.server.getClientCapabilities();
3+
export function hasRootsCapability(server: McpServer) {
4+
const capabilities = server.server.getClientCapabilities();
55

66
if (!capabilities) {
77
return false;
@@ -10,8 +10,8 @@ export function hasRootsCapability(context: McpContext) {
1010
return "roots" in capabilities && typeof capabilities.roots === "object";
1111
}
1212

13-
export function hasSamplingCapability(context: McpContext) {
14-
const capabilities = context.server.server.getClientCapabilities();
13+
export function hasSamplingCapability(server: McpServer) {
14+
const capabilities = server.server.getClientCapabilities();
1515

1616
if (!capabilities) {
1717
return false;
@@ -20,8 +20,8 @@ export function hasSamplingCapability(context: McpContext) {
2020
return "sampling" in capabilities && typeof capabilities.sampling === "object";
2121
}
2222

23-
export function hasElicitationCapability(context: McpContext) {
24-
const capabilities = context.server.server.getClientCapabilities();
23+
export function hasElicitationCapability(server: McpServer) {
24+
const capabilities = server.server.getClientCapabilities();
2525

2626
if (!capabilities) {
2727
return false;

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

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { VERSION } from "../version.js";
2+
3+
export const serverMetadata = {
4+
name: "trigger",
5+
version: VERSION,
6+
instructions: `Trigger.dev MCP server to automate your Trigger.dev projects and answer questions about Trigger.dev by searching the docs.
7+
If you need help setting up Trigger.dev in your project please refer to https://trigger.dev/docs/manual-setup.
8+
If the user asks for help with adding Trigger.dev to their project, please refer to https://trigger.dev/docs/manual-setup.
9+
`,
10+
};
11+
12+
export const toolsMetadata = {
13+
search_docs: {
14+
name: "search_docs",
15+
title: "Search Docs",
16+
description:
17+
"Search across the Trigger.dev documentation to find relevant information, code examples, API references, and guides. Use this tool when you need to answer questions about Trigger.dev, find specific documentation, understand how features work, or locate implementation details. The search returns contextual content with titles and direct links to the documentation pages",
18+
},
19+
list_projects: {
20+
name: "list_projects",
21+
title: "List Projects",
22+
description:
23+
"List all projects for the current user, useful for when searching for a project and for looking up a projectRef",
24+
},
25+
list_orgs: {
26+
name: "list_orgs",
27+
title: "List Organizations",
28+
description:
29+
"List all organizations for the current user. Useful when looking up an org slug or ID.",
30+
},
31+
create_project_in_org: {
32+
name: "create_project_in_org",
33+
title: "Create Project in Organization",
34+
description:
35+
"Create a new project in an organization. Only do this if the user wants to add Trigger.dev to an existing project. If there is already a trigger.config.ts file present, then you should not create a new project.",
36+
},
37+
initialize_project: {
38+
name: "initialize_project",
39+
title: "Initialize Project",
40+
description:
41+
"Initialize Trigger.dev in your project. This will create a new project in the organization you select and add Trigger.dev to your project.",
42+
},
43+
get_tasks: {
44+
name: "get_tasks",
45+
title: "Get Tasks",
46+
description:
47+
"Get all tasks in the project. Useful when searching for a task and for looking up a task identifier/slug",
48+
},
49+
trigger_task: {
50+
name: "trigger_task",
51+
title: "Trigger Task",
52+
description:
53+
"Trigger a task in the project. 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.",
54+
},
55+
get_run_details: {
56+
name: "get_run_details",
57+
title: "Get Run Details",
58+
description:
59+
"Get the details of a run. The run ID is the ID of the run that was triggered. It starts with run_",
60+
},
61+
cancel_run: {
62+
name: "cancel_run",
63+
title: "Cancel Run",
64+
description:
65+
"Cancel a run. The run ID is the ID of the run that was triggered. It starts with run_",
66+
},
67+
list_runs: {
68+
name: "list_runs",
69+
title: "List Runs",
70+
description:
71+
"List all runs for a project. Use this tool when you need to search for a run or list all runs for a project.",
72+
},
73+
deploy: {
74+
name: "deploy",
75+
title: "Deploy",
76+
description:
77+
"Deploy a project. Use this tool when you need to deploy a project. This will trigger a deployment for the project.",
78+
},
79+
list_deploys: {
80+
name: "list_deploys",
81+
title: "List Deploys",
82+
description:
83+
"List all deploys for a project. Use this tool when you need to search for a deploy or list all deploys for a project.",
84+
},
85+
list_preview_branches: {
86+
name: "list_preview_branches",
87+
title: "List Preview Branches",
88+
description:
89+
"List all preview branches for a project. Use this tool when you need to search for a preview branch or list all preview branches for a project.",
90+
},
91+
};

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

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22
import { FileLogger } from "./logger.js";
3+
import { createApiClientWithPublicJWT, mcpAuth } from "./auth.js";
4+
import { CliApiClient } from "../apiClient.js";
5+
import {
6+
hasElicitationCapability,
7+
hasRootsCapability,
8+
hasSamplingCapability,
9+
} from "./capabilities.js";
10+
import path from "node:path";
11+
import { tryCatch } from "@trigger.dev/core/utils";
12+
import { loadConfig } from "../config.js";
13+
import { ApiClient } from "@trigger.dev/core/v3";
314

415
export type McpContextOptions = {
516
projectRef?: string;
@@ -21,4 +32,159 @@ export class McpContext {
2132
get logger() {
2233
return this.options.fileLogger;
2334
}
35+
36+
public async getAuth() {
37+
const auth = await mcpAuth({
38+
server: this.server,
39+
defaultApiUrl: this.options.apiUrl,
40+
profile: this.options.profile,
41+
context: this,
42+
});
43+
44+
if (!auth.ok) {
45+
throw new Error(auth.error);
46+
}
47+
48+
return auth;
49+
}
50+
51+
public async getCliApiClient(branch?: string) {
52+
const auth = await this.getAuth();
53+
54+
return new CliApiClient(auth.auth.apiUrl, auth.auth.accessToken, branch);
55+
}
56+
57+
public async getApiClient(options: {
58+
projectRef: string;
59+
environment: string;
60+
scopes: string[];
61+
branch?: string;
62+
}) {
63+
const cliApiClient = await this.getCliApiClient(options.branch);
64+
65+
const jwt = await cliApiClient.getJWT(options.projectRef, options.environment, {
66+
claims: {
67+
scopes: options.scopes,
68+
},
69+
});
70+
71+
if (!jwt.success) {
72+
throw new Error(
73+
`Could not get the authentication token for the project ${options.projectRef} in the ${options.environment} environment. Please try again.`
74+
);
75+
}
76+
77+
return new ApiClient(cliApiClient.apiURL, jwt.data.token);
78+
}
79+
80+
public async getCwd() {
81+
if (!this.hasRootsCapability) {
82+
return undefined;
83+
}
84+
85+
const response = await this.server.server.listRoots();
86+
87+
if (response.roots.length >= 1) {
88+
return response.roots[0]?.uri ? fileUriToPath(response.roots[0].uri) : undefined;
89+
}
90+
91+
return undefined;
92+
}
93+
94+
public async getProjectRef(options: { projectRef?: string; cwd?: string }) {
95+
if (options.projectRef) {
96+
return options.projectRef;
97+
}
98+
99+
const projectDir = await this.getProjectDir({ cwd: options.cwd });
100+
101+
if (!projectDir.ok) {
102+
throw new Error(projectDir.error);
103+
}
104+
105+
const [_, config] = await tryCatch(loadConfig({ cwd: projectDir.cwd }));
106+
107+
if (
108+
config?.configFile &&
109+
typeof config.project === "string" &&
110+
config.project.startsWith("proj_")
111+
) {
112+
return config.project;
113+
}
114+
115+
throw new Error("No project ref found. Please provide a projectRef.");
116+
}
117+
118+
public async getProjectDir({ cwd }: { cwd?: string }) {
119+
// If cwd is a path to the actual trigger.config.ts file, then we should set the cwd to the directory of the file
120+
let $cwd = cwd ? (path.extname(cwd) !== "" ? path.dirname(cwd) : cwd) : undefined;
121+
122+
function isRelativePath(path: string) {
123+
return !path.startsWith("/");
124+
}
125+
126+
if (!cwd) {
127+
if (!this.hasRootsCapability) {
128+
return {
129+
ok: false,
130+
error:
131+
"The current MCP server does not support the roots capability, so please call the tool again with a projectRef or an absolute path as cwd parameter",
132+
};
133+
}
134+
135+
$cwd = await this.getCwd();
136+
} else if (isRelativePath(cwd)) {
137+
if (!this.hasRootsCapability) {
138+
return {
139+
ok: false,
140+
error:
141+
"The current MCP server does not support the roots capability, so please call the tool again with a projectRef or an absolute path as cwd parameter",
142+
};
143+
}
144+
145+
const resolvedCwd = await this.getCwd();
146+
147+
if (!resolvedCwd) {
148+
return {
149+
ok: false,
150+
error: "No current working directory found. Please provide a projectRef or a cwd.",
151+
};
152+
}
153+
154+
$cwd = path.resolve(resolvedCwd, cwd);
155+
}
156+
157+
if (!$cwd) {
158+
return {
159+
ok: false,
160+
error: "No current working directory found. Please provide a projectRef or a cwd.",
161+
};
162+
}
163+
164+
return {
165+
ok: true,
166+
cwd: $cwd,
167+
};
168+
}
169+
170+
public async getDashboardUrl(path: string) {
171+
const auth = await this.getAuth();
172+
return `${auth.dashboardUrl}${path}`;
173+
}
174+
175+
public get hasRootsCapability() {
176+
return hasRootsCapability(this.server);
177+
}
178+
179+
public get hasSamplingCapability() {
180+
return hasSamplingCapability(this.server);
181+
}
182+
183+
public get hasElicitationCapability() {
184+
return hasElicitationCapability(this.server);
185+
}
186+
}
187+
188+
function fileUriToPath(uri: string) {
189+
return uri.replace("file://", "");
24190
}

0 commit comments

Comments
 (0)