-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathmcp.ts
More file actions
170 lines (150 loc) · 5.57 KB
/
mcp.ts
File metadata and controls
170 lines (150 loc) · 5.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import { intro, outro } from "@clack/prompts";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { VERSION } from "@trigger.dev/core";
import { tryCatch } from "@trigger.dev/core/utils";
import { Command, Option as CommandOption } from "commander";
import { z } from "zod";
import { CommonCommandOptions, commonOptions, wrapCommandAction } from "../cli/common.js";
import { CLOUD_API_URL } from "../consts.js";
import { McpContext } from "../mcp/context.js";
import { FileLogger } from "../mcp/logger.js";
import { registerTools } from "../mcp/tools.js";
import { printStandloneInitialBanner } from "../utilities/initialBanner.js";
import { logger } from "../utilities/logger.js";
import { installMcpServer } from "./install-mcp.js";
import { serverMetadata } from "../mcp/config.js";
import { initiateRulesInstallWizard } from "./install-rules.js";
const McpCommandOptions = CommonCommandOptions.extend({
projectRef: z.string().optional(),
logFile: z.string().optional(),
devOnly: z.boolean().default(false),
envOnly: z.string().optional(),
disableDeployment: z.boolean().default(false),
readonly: z.boolean().default(false),
rulesInstallManifestPath: z.string().optional(),
rulesInstallBranch: z.string().optional(),
});
export type McpCommandOptions = z.infer<typeof McpCommandOptions>;
export function configureMcpCommand(program: Command) {
return commonOptions(
program
.command("mcp")
.description("Run the MCP server")
.option("-p, --project-ref <project ref>", "The project ref to use")
.option(
"--dev-only",
"Only run the MCP server for the dev environment. Attempts to access other environments will fail. (Deprecated: use --env-only dev instead)"
)
.option(
"--env-only <environments>",
"Restrict the MCP server to specific environments only. Comma-separated list of: dev, staging, prod, preview. Example: --env-only dev,staging"
)
.option(
"--disable-deployment",
"Disable deployment-related tools. When enabled, deployment tools won't be available. This option is overridden by --readonly."
)
.option(
"--readonly",
"Run in read-only mode. Disables all write operations including deployments, task triggering, and project creation. Overrides --disable-deployment."
)
.option("--log-file <log file>", "The file to log to")
.addOption(
new CommandOption(
"--rules-install-manifest-path <path>",
"The path to the rules install manifest"
).hideHelp()
)
.addOption(
new CommandOption(
"--rules-install-branch <branch>",
"The branch to install the rules from"
).hideHelp()
)
).action(async (options) => {
wrapCommandAction("mcp", McpCommandOptions, options, async (opts) => {
await mcpCommand(opts);
});
});
}
export async function mcpCommand(options: McpCommandOptions) {
if (process.stdout.isTTY) {
await printStandloneInitialBanner(true);
intro("Welcome to the Trigger.dev MCP server install wizard 🧙");
const [installError] = await tryCatch(
installMcpServer({
yolo: false,
tag: VERSION as string,
logLevel: "log",
})
);
if (installError) {
outro(`Failed to install MCP server: ${installError.message}`);
return;
}
await initiateRulesInstallWizard({
manifestPath: options.rulesInstallManifestPath,
branch: options.rulesInstallBranch,
});
return;
}
logger.loggerLevel = "none";
const server = new McpServer(
{
name: serverMetadata.name,
version: serverMetadata.version,
},
{
instructions: serverMetadata.instructions,
}
);
server.server.oninitialized = async () => {
fileLogger?.log("initialized mcp command", { options, argv: process.argv });
};
// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
const fileLogger: FileLogger | undefined = options.logFile
? new FileLogger(options.logFile, server)
: undefined;
// Check for mutual exclusivity
if (options.devOnly && options.envOnly) {
logger.error("Error: --dev-only and --env-only are mutually exclusive. Please use only one.");
process.exit(1);
}
// Parse envOnly into an array if provided
let envOnly: string[] | undefined;
if (options.envOnly) {
// Parse, normalize, and deduplicate environments
envOnly = Array.from(
new Set(
options.envOnly
.split(',')
.map(env => env.trim().toLowerCase())
.filter(Boolean) // Remove empty strings
)
);
// Validate environment names
const validEnvironments = ['dev', 'staging', 'prod', 'preview'];
const invalidEnvs = envOnly.filter(env => !validEnvironments.includes(env));
if (invalidEnvs.length > 0) {
logger.error(`Error: Invalid environment(s): ${invalidEnvs.join(', ')}`);
logger.error(`Valid environments are: ${validEnvironments.join(', ')}`);
process.exit(1);
}
} else if (options.devOnly) {
// For backward compatibility, convert devOnly to envOnly
envOnly = ['dev'];
}
const context = new McpContext(server, {
projectRef: options.projectRef,
fileLogger,
apiUrl: options.apiUrl ?? CLOUD_API_URL,
profile: options.profile,
devOnly: options.devOnly,
envOnly,
disableDeployment: options.disableDeployment,
readonly: options.readonly,
});
registerTools(context);
await server.connect(transport);
}