@getjack/jack 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -5
- package/src/commands/domain.ts +148 -17
- package/src/commands/domains.ts +28 -2
- package/src/commands/services.ts +389 -20
- package/src/commands/ship.ts +28 -3
- package/src/commands/skills.ts +58 -1
- package/src/commands/tokens.ts +119 -0
- package/src/commands/whoami.ts +10 -6
- package/src/index.ts +17 -0
- package/src/lib/auth/client.ts +11 -1
- package/src/lib/auth/guard.ts +1 -1
- package/src/lib/auth/login-flow.ts +34 -0
- package/src/lib/auth/store.ts +3 -0
- package/src/lib/control-plane.ts +156 -0
- package/src/lib/mcp-config.ts +26 -4
- package/src/lib/output.ts +4 -2
- package/src/lib/picker.ts +3 -1
- package/src/lib/project-operations.ts +38 -4
- package/src/lib/services/cron-create.ts +73 -0
- package/src/lib/services/cron-delete.ts +66 -0
- package/src/lib/services/cron-list.ts +59 -0
- package/src/lib/services/cron-test.ts +93 -0
- package/src/lib/services/cron-utils.ts +78 -0
- package/src/lib/services/domain-operations.ts +89 -18
- package/src/lib/services/token-operations.ts +84 -0
- package/src/lib/telemetry.ts +4 -0
- package/src/mcp/resources/index.ts +173 -0
- package/src/mcp/server.ts +20 -0
- package/src/mcp/tools/index.ts +279 -0
|
@@ -31,6 +31,13 @@ export function registerResources(
|
|
|
31
31
|
description: "Semantic information about jack's capabilities for AI agents",
|
|
32
32
|
mimeType: "application/json",
|
|
33
33
|
},
|
|
34
|
+
{
|
|
35
|
+
uri: "agents://workflows",
|
|
36
|
+
name: "Workflow Recipes",
|
|
37
|
+
description:
|
|
38
|
+
"Multi-step workflow templates for common tasks like creating APIs with databases, debugging production issues, and setting up cron jobs",
|
|
39
|
+
mimeType: "text/markdown",
|
|
40
|
+
},
|
|
34
41
|
],
|
|
35
42
|
};
|
|
36
43
|
});
|
|
@@ -160,6 +167,172 @@ Check for JACK.md in the project root for project-specific instructions.
|
|
|
160
167
|
};
|
|
161
168
|
}
|
|
162
169
|
|
|
170
|
+
if (uri === "agents://workflows") {
|
|
171
|
+
const workflows = `# Jack Workflow Recipes
|
|
172
|
+
|
|
173
|
+
Multi-step workflows for common tasks. Each workflow can be executed by an agent team or a single agent working sequentially.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 1. Create API with Database
|
|
178
|
+
|
|
179
|
+
Goal: Scaffold a new API, add a database, create tables, and verify.
|
|
180
|
+
|
|
181
|
+
\`\`\`
|
|
182
|
+
Step 1: Create project
|
|
183
|
+
jack new my-api --template api
|
|
184
|
+
|
|
185
|
+
Step 2: Add database
|
|
186
|
+
mcp__jack__create_database (or: jack services db create)
|
|
187
|
+
|
|
188
|
+
Step 3: Create schema
|
|
189
|
+
mcp__jack__execute_sql
|
|
190
|
+
sql: "CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP)"
|
|
191
|
+
allow_write: true
|
|
192
|
+
|
|
193
|
+
Step 4: Edit src/index.ts — add routes that use c.env.DB
|
|
194
|
+
|
|
195
|
+
Step 5: Deploy
|
|
196
|
+
mcp__jack__deploy_project (or: jack ship)
|
|
197
|
+
|
|
198
|
+
Step 6: Verify
|
|
199
|
+
curl https://<slug>.runjack.xyz/api/items
|
|
200
|
+
\`\`\`
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 2. Debug Production Issue
|
|
205
|
+
|
|
206
|
+
Goal: Identify and fix a bug reported in production.
|
|
207
|
+
|
|
208
|
+
\`\`\`
|
|
209
|
+
Step 1: Check current status
|
|
210
|
+
mcp__jack__get_project_status (or: jack info)
|
|
211
|
+
|
|
212
|
+
Step 2: Collect recent logs
|
|
213
|
+
mcp__jack__tail_logs with max_events: 100, duration_ms: 5000
|
|
214
|
+
|
|
215
|
+
Step 3: Inspect database state if relevant
|
|
216
|
+
mcp__jack__execute_sql
|
|
217
|
+
sql: "SELECT * FROM <table> WHERE <condition>"
|
|
218
|
+
|
|
219
|
+
Step 4: Read and fix the source code
|
|
220
|
+
|
|
221
|
+
Step 5: Deploy fix
|
|
222
|
+
mcp__jack__deploy_project (or: jack ship)
|
|
223
|
+
|
|
224
|
+
Step 6: Verify fix via logs
|
|
225
|
+
mcp__jack__tail_logs with max_events: 20, duration_ms: 3000
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 3. Add Scheduled Task (Cron)
|
|
231
|
+
|
|
232
|
+
Goal: Run code on a schedule (cleanup, sync, reports).
|
|
233
|
+
|
|
234
|
+
\`\`\`
|
|
235
|
+
Step 1: Add scheduled handler to src/index.ts
|
|
236
|
+
export default {
|
|
237
|
+
async fetch(request, env) { ... },
|
|
238
|
+
async scheduled(event, env, ctx) {
|
|
239
|
+
// your cron logic here
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
Or with Hono, add a POST /__scheduled route.
|
|
244
|
+
|
|
245
|
+
Step 2: Deploy the handler
|
|
246
|
+
mcp__jack__deploy_project (or: jack ship)
|
|
247
|
+
|
|
248
|
+
Step 3: Create cron schedule
|
|
249
|
+
mcp__jack__create_cron with expression: "0 * * * *"
|
|
250
|
+
(or: jack services cron create "0 * * * *")
|
|
251
|
+
|
|
252
|
+
Step 4: Verify schedule
|
|
253
|
+
mcp__jack__test_cron with expression: "0 * * * *"
|
|
254
|
+
Shows next 5 scheduled times.
|
|
255
|
+
|
|
256
|
+
Step 5: Monitor via logs
|
|
257
|
+
mcp__jack__tail_logs
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## 4. Add File Storage
|
|
263
|
+
|
|
264
|
+
Goal: Enable file uploads and downloads via R2.
|
|
265
|
+
|
|
266
|
+
\`\`\`
|
|
267
|
+
Step 1: Create storage bucket
|
|
268
|
+
mcp__jack__create_storage_bucket (or: jack services storage create)
|
|
269
|
+
|
|
270
|
+
Step 2: Deploy to activate binding
|
|
271
|
+
mcp__jack__deploy_project (or: jack ship)
|
|
272
|
+
|
|
273
|
+
Step 3: Add upload/download routes to src/index.ts
|
|
274
|
+
Upload: c.env.BUCKET.put(key, body)
|
|
275
|
+
Download: c.env.BUCKET.get(key)
|
|
276
|
+
List: c.env.BUCKET.list()
|
|
277
|
+
|
|
278
|
+
Step 4: Deploy routes
|
|
279
|
+
mcp__jack__deploy_project (or: jack ship)
|
|
280
|
+
\`\`\`
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## 5. Add Semantic Search (Vectorize + AI)
|
|
285
|
+
|
|
286
|
+
Goal: Enable vector similarity search using embeddings.
|
|
287
|
+
|
|
288
|
+
\`\`\`
|
|
289
|
+
Step 1: Create vector index
|
|
290
|
+
mcp__jack__create_vectorize_index (or: jack services vectorize create)
|
|
291
|
+
Default: 768 dimensions, cosine metric (matches bge-base-en-v1.5)
|
|
292
|
+
|
|
293
|
+
Step 2: Deploy to activate binding
|
|
294
|
+
mcp__jack__deploy_project (or: jack ship)
|
|
295
|
+
|
|
296
|
+
Step 3: Add indexing route
|
|
297
|
+
Generate embedding: c.env.AI.run("@cf/baai/bge-base-en-v1.5", { text: [input] })
|
|
298
|
+
Insert: c.env.VECTORIZE_INDEX.insert([{ id, values, metadata }])
|
|
299
|
+
|
|
300
|
+
Step 4: Add search route
|
|
301
|
+
Generate query embedding, then:
|
|
302
|
+
c.env.VECTORIZE_INDEX.query(embedding, { topK: 5, returnMetadata: "all" })
|
|
303
|
+
|
|
304
|
+
Step 5: Deploy and test
|
|
305
|
+
mcp__jack__deploy_project (or: jack ship)
|
|
306
|
+
\`\`\`
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Parallel Task Patterns (Agent Teams)
|
|
311
|
+
|
|
312
|
+
For Opus 4.6 Agent Teams, these tasks can run in parallel:
|
|
313
|
+
|
|
314
|
+
**Independent (run simultaneously):**
|
|
315
|
+
- Creating database + creating storage bucket
|
|
316
|
+
- Reading logs + checking project status
|
|
317
|
+
- Multiple SQL queries on different tables
|
|
318
|
+
|
|
319
|
+
**Sequential (must wait for previous):**
|
|
320
|
+
- Create project → then add services
|
|
321
|
+
- Create database → then create tables → then deploy
|
|
322
|
+
- Edit code → then deploy → then verify
|
|
323
|
+
`;
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
contents: [
|
|
327
|
+
{
|
|
328
|
+
uri,
|
|
329
|
+
mimeType: "text/markdown",
|
|
330
|
+
text: workflows,
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
163
336
|
throw new Error(`Unknown resource URI: ${uri}`);
|
|
164
337
|
});
|
|
165
338
|
}
|
package/src/mcp/server.ts
CHANGED
|
@@ -49,6 +49,20 @@ export async function startMcpServer(options: McpServerOptions = {}) {
|
|
|
49
49
|
|
|
50
50
|
debug("Starting MCP server on stdio transport");
|
|
51
51
|
|
|
52
|
+
// Process-level error handlers to prevent silent crashes
|
|
53
|
+
process.on("uncaughtException", (error) => {
|
|
54
|
+
console.error(`[jack-mcp] Uncaught exception: ${error.message}`);
|
|
55
|
+
debug("Uncaught exception", { error: error.stack });
|
|
56
|
+
process.exit(1);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
process.on("unhandledRejection", (reason) => {
|
|
60
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
61
|
+
console.error(`[jack-mcp] Unhandled rejection: ${message}`);
|
|
62
|
+
debug("Unhandled rejection", { reason });
|
|
63
|
+
process.exit(1);
|
|
64
|
+
});
|
|
65
|
+
|
|
52
66
|
// Always log startup to stderr so user knows it's running
|
|
53
67
|
console.error(
|
|
54
68
|
`[jack-mcp] Server started (v${pkg.version})${options.debug ? " [debug mode]" : ""}`,
|
|
@@ -57,4 +71,10 @@ export async function startMcpServer(options: McpServerOptions = {}) {
|
|
|
57
71
|
await server.connect(transport);
|
|
58
72
|
|
|
59
73
|
debug("MCP server connected and ready");
|
|
74
|
+
|
|
75
|
+
// Keep the server running indefinitely.
|
|
76
|
+
// This blocks the async function from returning, preventing the caller from
|
|
77
|
+
// falling through to any cleanup/exit code (like process.exit(0) in index.ts).
|
|
78
|
+
// The process will stay alive via stdin event listeners in the transport.
|
|
79
|
+
await new Promise(() => {});
|
|
60
80
|
}
|
package/src/mcp/tools/index.ts
CHANGED
|
@@ -15,6 +15,10 @@ import {
|
|
|
15
15
|
wrapResultsForMcp,
|
|
16
16
|
} from "../../lib/services/db-execute.ts";
|
|
17
17
|
import { listDatabases } from "../../lib/services/db-list.ts";
|
|
18
|
+
import { createCronSchedule } from "../../lib/services/cron-create.ts";
|
|
19
|
+
import { deleteCronSchedule } from "../../lib/services/cron-delete.ts";
|
|
20
|
+
import { listCronSchedules } from "../../lib/services/cron-list.ts";
|
|
21
|
+
import { testCronExpression } from "../../lib/services/cron-test.ts";
|
|
18
22
|
import {
|
|
19
23
|
assignDomain,
|
|
20
24
|
connectDomain,
|
|
@@ -215,6 +219,42 @@ const DisconnectDomainSchema = z.object({
|
|
|
215
219
|
hostname: z.string().describe("The domain hostname to disconnect (fully remove)"),
|
|
216
220
|
});
|
|
217
221
|
|
|
222
|
+
const CreateCronSchema = z.object({
|
|
223
|
+
expression: z.string().describe("Cron expression (e.g., '0 * * * *' for hourly, '*/15 * * * *' for every 15 min)"),
|
|
224
|
+
project_path: z
|
|
225
|
+
.string()
|
|
226
|
+
.optional()
|
|
227
|
+
.describe("Path to project directory (defaults to current directory)"),
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const ListCronsSchema = z.object({
|
|
231
|
+
project_path: z
|
|
232
|
+
.string()
|
|
233
|
+
.optional()
|
|
234
|
+
.describe("Path to project directory (defaults to current directory)"),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const DeleteCronSchema = z.object({
|
|
238
|
+
expression: z.string().describe("Cron expression to delete"),
|
|
239
|
+
project_path: z
|
|
240
|
+
.string()
|
|
241
|
+
.optional()
|
|
242
|
+
.describe("Path to project directory (defaults to current directory)"),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const TestCronSchema = z.object({
|
|
246
|
+
expression: z.string().describe("Cron expression to test"),
|
|
247
|
+
project_path: z
|
|
248
|
+
.string()
|
|
249
|
+
.optional()
|
|
250
|
+
.describe("Path to project directory (defaults to current directory)"),
|
|
251
|
+
trigger_production: z
|
|
252
|
+
.boolean()
|
|
253
|
+
.optional()
|
|
254
|
+
.default(false)
|
|
255
|
+
.describe("Whether to trigger the cron handler on production (requires managed project)"),
|
|
256
|
+
});
|
|
257
|
+
|
|
218
258
|
export function registerTools(server: McpServer, _options: McpServerOptions, debug: DebugLogger) {
|
|
219
259
|
// Register tool list handler
|
|
220
260
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -611,6 +651,80 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
611
651
|
required: ["hostname"],
|
|
612
652
|
},
|
|
613
653
|
},
|
|
654
|
+
{
|
|
655
|
+
name: "create_cron",
|
|
656
|
+
description:
|
|
657
|
+
"Create a cron schedule for a managed (Jack Cloud) project. Minimum interval is 15 minutes. The worker must have a scheduled() handler or POST /__scheduled route.",
|
|
658
|
+
inputSchema: {
|
|
659
|
+
type: "object",
|
|
660
|
+
properties: {
|
|
661
|
+
expression: {
|
|
662
|
+
type: "string",
|
|
663
|
+
description: "Cron expression (e.g., '0 * * * *' for hourly, '*/15 * * * *' for every 15 min)",
|
|
664
|
+
},
|
|
665
|
+
project_path: {
|
|
666
|
+
type: "string",
|
|
667
|
+
description: "Path to project directory (defaults to current directory)",
|
|
668
|
+
},
|
|
669
|
+
},
|
|
670
|
+
required: ["expression"],
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
name: "list_crons",
|
|
675
|
+
description: "List all cron schedules for a managed (Jack Cloud) project.",
|
|
676
|
+
inputSchema: {
|
|
677
|
+
type: "object",
|
|
678
|
+
properties: {
|
|
679
|
+
project_path: {
|
|
680
|
+
type: "string",
|
|
681
|
+
description: "Path to project directory (defaults to current directory)",
|
|
682
|
+
},
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
name: "delete_cron",
|
|
688
|
+
description: "Delete a cron schedule by its expression.",
|
|
689
|
+
inputSchema: {
|
|
690
|
+
type: "object",
|
|
691
|
+
properties: {
|
|
692
|
+
expression: {
|
|
693
|
+
type: "string",
|
|
694
|
+
description: "Cron expression to delete",
|
|
695
|
+
},
|
|
696
|
+
project_path: {
|
|
697
|
+
type: "string",
|
|
698
|
+
description: "Path to project directory (defaults to current directory)",
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
required: ["expression"],
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
name: "test_cron",
|
|
706
|
+
description:
|
|
707
|
+
"Test a cron expression: validate, show human-readable description, and display next 5 scheduled times. Optionally trigger the handler on production.",
|
|
708
|
+
inputSchema: {
|
|
709
|
+
type: "object",
|
|
710
|
+
properties: {
|
|
711
|
+
expression: {
|
|
712
|
+
type: "string",
|
|
713
|
+
description: "Cron expression to test",
|
|
714
|
+
},
|
|
715
|
+
project_path: {
|
|
716
|
+
type: "string",
|
|
717
|
+
description: "Path to project directory (defaults to current directory)",
|
|
718
|
+
},
|
|
719
|
+
trigger_production: {
|
|
720
|
+
type: "boolean",
|
|
721
|
+
default: false,
|
|
722
|
+
description: "Whether to trigger the cron handler on production (requires managed project)",
|
|
723
|
+
},
|
|
724
|
+
},
|
|
725
|
+
required: ["expression"],
|
|
726
|
+
},
|
|
727
|
+
},
|
|
614
728
|
],
|
|
615
729
|
};
|
|
616
730
|
});
|
|
@@ -1566,6 +1680,171 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
|
|
|
1566
1680
|
};
|
|
1567
1681
|
}
|
|
1568
1682
|
|
|
1683
|
+
case "create_cron": {
|
|
1684
|
+
const args = CreateCronSchema.parse(request.params.arguments ?? {});
|
|
1685
|
+
const projectPath = args.project_path ?? process.cwd();
|
|
1686
|
+
|
|
1687
|
+
const wrappedCreateCron = withTelemetry(
|
|
1688
|
+
"create_cron",
|
|
1689
|
+
async (projectDir: string, expression: string) => {
|
|
1690
|
+
const result = await createCronSchedule(projectDir, expression, {
|
|
1691
|
+
interactive: false,
|
|
1692
|
+
});
|
|
1693
|
+
|
|
1694
|
+
track(Events.SERVICE_CREATED, {
|
|
1695
|
+
service_type: "cron",
|
|
1696
|
+
platform: "mcp",
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
return {
|
|
1700
|
+
id: result.id,
|
|
1701
|
+
expression: result.expression,
|
|
1702
|
+
description: result.description,
|
|
1703
|
+
next_run_at: result.nextRunAt,
|
|
1704
|
+
created: result.created,
|
|
1705
|
+
};
|
|
1706
|
+
},
|
|
1707
|
+
{ platform: "mcp" },
|
|
1708
|
+
);
|
|
1709
|
+
|
|
1710
|
+
const result = await wrappedCreateCron(projectPath, args.expression);
|
|
1711
|
+
|
|
1712
|
+
return {
|
|
1713
|
+
content: [
|
|
1714
|
+
{
|
|
1715
|
+
type: "text",
|
|
1716
|
+
text: JSON.stringify(formatSuccessResponse(result, startTime), null, 2),
|
|
1717
|
+
},
|
|
1718
|
+
],
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
case "list_crons": {
|
|
1723
|
+
const args = ListCronsSchema.parse(request.params.arguments ?? {});
|
|
1724
|
+
const projectPath = args.project_path ?? process.cwd();
|
|
1725
|
+
|
|
1726
|
+
const wrappedListCrons = withTelemetry(
|
|
1727
|
+
"list_crons",
|
|
1728
|
+
async (projectDir: string) => {
|
|
1729
|
+
const schedules = await listCronSchedules(projectDir);
|
|
1730
|
+
return {
|
|
1731
|
+
schedules: schedules.map((s) => ({
|
|
1732
|
+
id: s.id,
|
|
1733
|
+
expression: s.expression,
|
|
1734
|
+
description: s.description,
|
|
1735
|
+
enabled: s.enabled,
|
|
1736
|
+
next_run_at: s.nextRunAt,
|
|
1737
|
+
last_run_at: s.lastRunAt,
|
|
1738
|
+
last_run_status: s.lastRunStatus,
|
|
1739
|
+
last_run_duration_ms: s.lastRunDurationMs,
|
|
1740
|
+
consecutive_failures: s.consecutiveFailures,
|
|
1741
|
+
created_at: s.createdAt,
|
|
1742
|
+
})),
|
|
1743
|
+
};
|
|
1744
|
+
},
|
|
1745
|
+
{ platform: "mcp" },
|
|
1746
|
+
);
|
|
1747
|
+
|
|
1748
|
+
const result = await wrappedListCrons(projectPath);
|
|
1749
|
+
|
|
1750
|
+
return {
|
|
1751
|
+
content: [
|
|
1752
|
+
{
|
|
1753
|
+
type: "text",
|
|
1754
|
+
text: JSON.stringify(formatSuccessResponse(result, startTime), null, 2),
|
|
1755
|
+
},
|
|
1756
|
+
],
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
case "delete_cron": {
|
|
1761
|
+
const args = DeleteCronSchema.parse(request.params.arguments ?? {});
|
|
1762
|
+
const projectPath = args.project_path ?? process.cwd();
|
|
1763
|
+
|
|
1764
|
+
const wrappedDeleteCron = withTelemetry(
|
|
1765
|
+
"delete_cron",
|
|
1766
|
+
async (projectDir: string, expression: string) => {
|
|
1767
|
+
const result = await deleteCronSchedule(projectDir, expression, {
|
|
1768
|
+
interactive: false,
|
|
1769
|
+
});
|
|
1770
|
+
|
|
1771
|
+
track(Events.SERVICE_DELETED, {
|
|
1772
|
+
service_type: "cron",
|
|
1773
|
+
platform: "mcp",
|
|
1774
|
+
});
|
|
1775
|
+
|
|
1776
|
+
return {
|
|
1777
|
+
expression: result.expression,
|
|
1778
|
+
deleted: result.deleted,
|
|
1779
|
+
};
|
|
1780
|
+
},
|
|
1781
|
+
{ platform: "mcp" },
|
|
1782
|
+
);
|
|
1783
|
+
|
|
1784
|
+
const result = await wrappedDeleteCron(projectPath, args.expression);
|
|
1785
|
+
|
|
1786
|
+
return {
|
|
1787
|
+
content: [
|
|
1788
|
+
{
|
|
1789
|
+
type: "text",
|
|
1790
|
+
text: JSON.stringify(formatSuccessResponse(result, startTime), null, 2),
|
|
1791
|
+
},
|
|
1792
|
+
],
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
case "test_cron": {
|
|
1797
|
+
const args = TestCronSchema.parse(request.params.arguments ?? {});
|
|
1798
|
+
const projectPath = args.project_path ?? process.cwd();
|
|
1799
|
+
|
|
1800
|
+
const wrappedTestCron = withTelemetry(
|
|
1801
|
+
"test_cron",
|
|
1802
|
+
async (projectDir: string, expression: string, triggerProduction: boolean) => {
|
|
1803
|
+
const result = await testCronExpression(projectDir, expression, {
|
|
1804
|
+
triggerProduction,
|
|
1805
|
+
interactive: false,
|
|
1806
|
+
});
|
|
1807
|
+
|
|
1808
|
+
if (!result.valid) {
|
|
1809
|
+
return {
|
|
1810
|
+
valid: false,
|
|
1811
|
+
error: result.error,
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
return {
|
|
1816
|
+
valid: true,
|
|
1817
|
+
expression: result.expression,
|
|
1818
|
+
description: result.description,
|
|
1819
|
+
next_times: result.nextTimes?.map((d) => d.toISOString()),
|
|
1820
|
+
trigger_result: result.triggerResult
|
|
1821
|
+
? {
|
|
1822
|
+
triggered: result.triggerResult.triggered,
|
|
1823
|
+
status: result.triggerResult.status,
|
|
1824
|
+
duration_ms: result.triggerResult.durationMs,
|
|
1825
|
+
}
|
|
1826
|
+
: undefined,
|
|
1827
|
+
};
|
|
1828
|
+
},
|
|
1829
|
+
{ platform: "mcp" },
|
|
1830
|
+
);
|
|
1831
|
+
|
|
1832
|
+
const result = await wrappedTestCron(
|
|
1833
|
+
projectPath,
|
|
1834
|
+
args.expression,
|
|
1835
|
+
args.trigger_production ?? false,
|
|
1836
|
+
);
|
|
1837
|
+
|
|
1838
|
+
return {
|
|
1839
|
+
content: [
|
|
1840
|
+
{
|
|
1841
|
+
type: "text",
|
|
1842
|
+
text: JSON.stringify(formatSuccessResponse(result, startTime), null, 2),
|
|
1843
|
+
},
|
|
1844
|
+
],
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1569
1848
|
default:
|
|
1570
1849
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
1571
1850
|
}
|