@getjack/jack 0.1.16 → 0.1.19

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.
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Zip utility functions for extracting zip archives
3
+ */
4
+
5
+ import { mkdir, writeFile } from "node:fs/promises";
6
+ import { dirname, join } from "node:path";
7
+ import { unzipSync } from "fflate";
8
+
9
+ /**
10
+ * Extract a zip buffer to a target directory.
11
+ * Creates directories as needed and writes files preserving relative paths.
12
+ *
13
+ * @param zipBuffer - The zip file contents as a Buffer
14
+ * @param targetDir - The directory to extract files to
15
+ * @returns The number of files extracted
16
+ */
17
+ export async function extractZipToDirectory(zipBuffer: Buffer, targetDir: string): Promise<number> {
18
+ const unzipped = unzipSync(new Uint8Array(zipBuffer));
19
+ let fileCount = 0;
20
+
21
+ for (const [path, content] of Object.entries(unzipped)) {
22
+ // Skip directories (they end with /)
23
+ if (path.endsWith("/")) continue;
24
+
25
+ // Security: prevent path traversal by removing any .. segments
26
+ const normalizedPath = path.replace(/\.\./g, "");
27
+ const fullPath = join(targetDir, normalizedPath);
28
+
29
+ // Ensure directory exists
30
+ await mkdir(dirname(fullPath), { recursive: true });
31
+
32
+ // Write file
33
+ await writeFile(fullPath, content);
34
+ fileCount++;
35
+ }
36
+
37
+ return fileCount;
38
+ }
@@ -5,6 +5,12 @@ import { JackError, JackErrorCode } from "../../lib/errors.ts";
5
5
  import { createProject, deployProject, getProjectStatus } from "../../lib/project-operations.ts";
6
6
  import { listAllProjects } from "../../lib/project-resolver.ts";
7
7
  import { createDatabase } from "../../lib/services/db-create.ts";
8
+ import {
9
+ DestructiveOperationError,
10
+ WriteNotAllowedError,
11
+ executeSql,
12
+ wrapResultsForMcp,
13
+ } from "../../lib/services/db-execute.ts";
8
14
  import { listDatabases } from "../../lib/services/db-list.ts";
9
15
  import { Events, track, withTelemetry } from "../../lib/telemetry.ts";
10
16
  import type { DebugLogger, McpServerOptions } from "../types.ts";
@@ -53,6 +59,25 @@ const ListDatabasesSchema = z.object({
53
59
  .describe("Path to project directory (defaults to current directory)"),
54
60
  });
55
61
 
62
+ const ExecuteSqlSchema = z.object({
63
+ sql: z.string().describe("SQL query to execute"),
64
+ project_path: z
65
+ .string()
66
+ .optional()
67
+ .describe("Path to project directory (defaults to current directory)"),
68
+ allow_write: z
69
+ .boolean()
70
+ .optional()
71
+ .default(false)
72
+ .describe(
73
+ "Allow write operations (INSERT, UPDATE, DELETE). Required for any data modification. Destructive operations (DROP, TRUNCATE) are blocked and must be run via CLI.",
74
+ ),
75
+ database_name: z
76
+ .string()
77
+ .optional()
78
+ .describe("Database name (auto-detect from wrangler.jsonc if not provided)"),
79
+ });
80
+
56
81
  export function registerTools(server: McpServer, _options: McpServerOptions, debug: DebugLogger) {
57
82
  // Register tool list handler
58
83
  server.setRequestHandler(ListToolsRequestSchema, async () => {
@@ -155,6 +180,38 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
155
180
  },
156
181
  },
157
182
  },
183
+ {
184
+ name: "execute_sql",
185
+ description:
186
+ "Execute SQL against the project's D1 database. Read-only by default for safety. " +
187
+ "Set allow_write=true for INSERT, UPDATE, DELETE operations. " +
188
+ "Destructive operations (DROP, TRUNCATE, ALTER) are blocked and must be run via CLI with confirmation. " +
189
+ "Results are wrapped with anti-injection headers to prevent prompt injection from database content.",
190
+ inputSchema: {
191
+ type: "object",
192
+ properties: {
193
+ sql: {
194
+ type: "string",
195
+ description: "SQL query to execute",
196
+ },
197
+ project_path: {
198
+ type: "string",
199
+ description: "Path to project directory (defaults to current directory)",
200
+ },
201
+ allow_write: {
202
+ type: "boolean",
203
+ default: false,
204
+ description:
205
+ "Allow write operations (INSERT, UPDATE, DELETE). Required for any data modification.",
206
+ },
207
+ database_name: {
208
+ type: "string",
209
+ description: "Database name (auto-detect from wrangler.jsonc if not provided)",
210
+ },
211
+ },
212
+ required: ["sql"],
213
+ },
214
+ },
158
215
  ],
159
216
  };
160
217
  });
@@ -389,6 +446,110 @@ export function registerTools(server: McpServer, _options: McpServerOptions, deb
389
446
  };
390
447
  }
391
448
 
449
+ case "execute_sql": {
450
+ const args = ExecuteSqlSchema.parse(request.params.arguments ?? {});
451
+ const projectPath = args.project_path ?? process.cwd();
452
+
453
+ const wrappedExecuteSql = withTelemetry(
454
+ "execute_sql",
455
+ async (projectDir: string, sql: string, allowWrite: boolean, databaseName?: string) => {
456
+ try {
457
+ const result = await executeSql({
458
+ projectDir,
459
+ sql,
460
+ databaseName,
461
+ allowWrite,
462
+ interactive: false, // MCP is non-interactive
463
+ wrapResults: true,
464
+ });
465
+
466
+ // Track business event
467
+ track(Events.SQL_EXECUTED, {
468
+ risk_level: result.risk,
469
+ statement_count: result.statements.length,
470
+ platform: "mcp",
471
+ });
472
+
473
+ // Wrap results with anti-injection header for MCP
474
+ const wrappedContent = wrapResultsForMcp(result.results ?? [], sql, result.meta);
475
+
476
+ return {
477
+ success: result.success,
478
+ risk_level: result.risk,
479
+ results_wrapped: wrappedContent,
480
+ meta: result.meta,
481
+ warning: result.warning,
482
+ };
483
+ } catch (err) {
484
+ if (err instanceof WriteNotAllowedError) {
485
+ return {
486
+ success: false,
487
+ error: err.message,
488
+ suggestion: "Set allow_write=true to allow data modification",
489
+ risk_level: err.risk,
490
+ };
491
+ }
492
+ if (err instanceof DestructiveOperationError) {
493
+ return {
494
+ success: false,
495
+ error: err.message,
496
+ suggestion:
497
+ "Destructive operations (DROP, TRUNCATE, ALTER, DELETE without WHERE) " +
498
+ "must be run via CLI with explicit confirmation: " +
499
+ `jack services db execute "${sql.slice(0, 50)}..." --write`,
500
+ risk_level: "destructive",
501
+ };
502
+ }
503
+ throw err;
504
+ }
505
+ },
506
+ { platform: "mcp" },
507
+ );
508
+
509
+ const result = await wrappedExecuteSql(
510
+ projectPath,
511
+ args.sql,
512
+ args.allow_write ?? false,
513
+ args.database_name,
514
+ );
515
+
516
+ // Check if the result indicates an error (e.g., WriteNotAllowedError, DestructiveOperationError)
517
+ // These are returned as objects with success: false instead of thrown
518
+ const resultObj = result as Record<string, unknown>;
519
+ if (resultObj?.success === false) {
520
+ return {
521
+ content: [
522
+ {
523
+ type: "text",
524
+ text: JSON.stringify(
525
+ {
526
+ success: false,
527
+ error: resultObj.error,
528
+ suggestion: resultObj.suggestion,
529
+ risk_level: resultObj.risk_level,
530
+ meta: {
531
+ duration_ms: Date.now() - startTime,
532
+ },
533
+ },
534
+ null,
535
+ 2,
536
+ ),
537
+ },
538
+ ],
539
+ isError: true,
540
+ };
541
+ }
542
+
543
+ return {
544
+ content: [
545
+ {
546
+ type: "text",
547
+ text: JSON.stringify(formatSuccessResponse(result, startTime), null, 2),
548
+ },
549
+ ],
550
+ };
551
+ }
552
+
392
553
  default:
393
554
  throw new Error(`Unknown tool: ${toolName}`);
394
555
  }
@@ -73,6 +73,7 @@ async function loadTemplate(name: string): Promise<Template> {
73
73
  description: string;
74
74
  secrets: string[];
75
75
  optionalSecrets?: Template["optionalSecrets"];
76
+ envVars?: Template["envVars"];
76
77
  capabilities?: Template["capabilities"];
77
78
  requires?: Template["requires"];
78
79
  hooks?: Template["hooks"];
@@ -90,6 +91,7 @@ async function loadTemplate(name: string): Promise<Template> {
90
91
  description: metadata.description,
91
92
  secrets: metadata.secrets,
92
93
  optionalSecrets: metadata.optionalSecrets,
94
+ envVars: metadata.envVars,
93
95
  capabilities: metadata.capabilities,
94
96
  requires: metadata.requires,
95
97
  hooks: metadata.hooks,
@@ -165,6 +167,7 @@ async function fetchPublishedTemplate(username: string, slug: string): Promise<T
165
167
  description: (metadata.description as string) || `Fork of ${username}/${slug}`,
166
168
  secrets: (metadata.secrets as string[]) || [],
167
169
  optionalSecrets: metadata.optionalSecrets as Template["optionalSecrets"],
170
+ envVars: metadata.envVars as Template["envVars"],
168
171
  capabilities: metadata.capabilities as Template["capabilities"],
169
172
  requires: metadata.requires as Template["requires"],
170
173
  hooks: metadata.hooks as Template["hooks"],
@@ -200,6 +203,7 @@ async function fetchUserTemplate(slug: string): Promise<Template | null> {
200
203
  description: (metadata.description as string) || `Your project: ${slug}`,
201
204
  secrets: (metadata.secrets as string[]) || [],
202
205
  optionalSecrets: metadata.optionalSecrets as Template["optionalSecrets"],
206
+ envVars: metadata.envVars as Template["envVars"],
203
207
  capabilities: metadata.capabilities as Template["capabilities"],
204
208
  requires: metadata.requires as Template["requires"],
205
209
  hooks: metadata.hooks as Template["hooks"],
@@ -16,6 +16,8 @@ export type HookAction =
16
16
  path: string;
17
17
  set: Record<string, string | { from: "input" }>;
18
18
  };
19
+ deployAfter?: boolean; // Redeploy after successful input (only if user provided input)
20
+ deployMessage?: string; // Message to show during deploy (default: "Deploying...")
19
21
  }
20
22
  | {
21
23
  action: "writeJson";
@@ -53,6 +55,15 @@ export interface OptionalSecret {
53
55
  setupUrl?: string;
54
56
  }
55
57
 
58
+ export interface EnvVar {
59
+ name: string;
60
+ description: string;
61
+ required?: boolean;
62
+ defaultValue?: string;
63
+ example?: string;
64
+ setupUrl?: string;
65
+ }
66
+
56
67
  export interface IntentMetadata {
57
68
  keywords: string[];
58
69
  examples?: string[]; // For future telemetry/docs
@@ -62,6 +73,7 @@ export interface Template {
62
73
  files: Record<string, string>; // path -> content
63
74
  secrets?: string[]; // required secret keys (e.g., ["NEYNAR_API_KEY"])
64
75
  optionalSecrets?: OptionalSecret[]; // optional secret configurations
76
+ envVars?: EnvVar[]; // environment variables (non-secret config)
65
77
  capabilities?: Capability[]; // infrastructure requirements (deprecated, use requires)
66
78
  requires?: ServiceTypeKey[]; // service requirements (DB, KV, CRON, QUEUE, STORAGE)
67
79
  description?: string; // for help text
@@ -0,0 +1,33 @@
1
+ # Agent Context
2
+
3
+ ## Infrastructure
4
+
5
+ This project uses **jack** for deployment. When Jack MCP is connected, prefer `mcp__jack__*` tools over CLI commands - they're cloud-aware and faster.
6
+
7
+ **Do NOT use wrangler directly.** Jack manages config and may use cloud-hosted resources where wrangler won't work.
8
+
9
+ Common operations:
10
+ - **Deploy**: `jack ship` or Jack MCP
11
+ - **Database**: `jack services db create` or Jack MCP
12
+ - **Status**: `jack status` or Jack MCP
13
+
14
+ ## SQL Execution
15
+
16
+ Jack supports secure SQL execution against D1 databases:
17
+
18
+ **Via MCP** (preferred for agents):
19
+ - `execute_sql({ sql: "SELECT * FROM users" })` - read queries work by default
20
+ - `execute_sql({ sql: "INSERT...", allow_write: true })` - writes require allow_write
21
+ - Destructive ops (DROP, TRUNCATE, ALTER) are blocked via MCP - use CLI
22
+
23
+ **Via CLI**:
24
+ - `jack services db execute "SELECT * FROM users"` - read queries
25
+ - `jack services db execute "INSERT..." --write` - write queries
26
+ - `jack services db execute "DROP TABLE..." --write` - prompts for typed confirmation
27
+ - `jack services db execute --file schema.sql --write` - run SQL from file
28
+
29
+ **Security notes**:
30
+ - Read-only by default to prevent accidental data modification
31
+ - Write operations require explicit `--write` flag or `allow_write: true`
32
+ - Destructive operations (DROP, TRUNCATE, ALTER, DELETE without WHERE) require CLI confirmation
33
+ - MCP results are wrapped with anti-injection headers to prevent prompt injection
@@ -0,0 +1,33 @@
1
+ # Agent Context
2
+
3
+ ## Infrastructure
4
+
5
+ This project uses **jack** for deployment. When Jack MCP is connected, prefer `mcp__jack__*` tools over CLI commands - they're cloud-aware and faster.
6
+
7
+ **Do NOT use wrangler directly.** Jack manages config and may use cloud-hosted resources where wrangler won't work.
8
+
9
+ Common operations:
10
+ - **Deploy**: `jack ship` or Jack MCP
11
+ - **Database**: `jack services db create` or Jack MCP
12
+ - **Status**: `jack status` or Jack MCP
13
+
14
+ ## SQL Execution
15
+
16
+ Jack supports secure SQL execution against D1 databases:
17
+
18
+ **Via MCP** (preferred for agents):
19
+ - `execute_sql({ sql: "SELECT * FROM users" })` - read queries work by default
20
+ - `execute_sql({ sql: "INSERT...", allow_write: true })` - writes require allow_write
21
+ - Destructive ops (DROP, TRUNCATE, ALTER) are blocked via MCP - use CLI
22
+
23
+ **Via CLI**:
24
+ - `jack services db execute "SELECT * FROM users"` - read queries
25
+ - `jack services db execute "INSERT..." --write` - write queries
26
+ - `jack services db execute "DROP TABLE..." --write` - prompts for typed confirmation
27
+ - `jack services db execute --file schema.sql --write` - run SQL from file
28
+
29
+ **Security notes**:
30
+ - Read-only by default to prevent accidental data modification
31
+ - Write operations require explicit `--write` flag or `allow_write: true`
32
+ - Destructive operations (DROP, TRUNCATE, ALTER, DELETE without WHERE) require CLI confirmation
33
+ - MCP results are wrapped with anti-injection headers to prevent prompt injection
@@ -39,10 +39,7 @@
39
39
  "action": "box",
40
40
  "title": "Deployed: {{name}}",
41
41
  "lines": [
42
- "URL: {{url}}",
43
- "Manifest: {{url}}/.well-known/farcaster.json",
44
- "",
45
- "Next: Sign the manifest and paste accountAssociation when prompted"
42
+ "{{url}}"
46
43
  ]
47
44
  },
48
45
  {
@@ -72,7 +69,9 @@
72
69
  "set": {
73
70
  "accountAssociation": { "from": "input" }
74
71
  }
75
- }
72
+ },
73
+ "deployAfter": true,
74
+ "deployMessage": "Deploying manifest..."
76
75
  },
77
76
  {
78
77
  "action": "url",
@@ -0,0 +1,33 @@
1
+ # Agent Context
2
+
3
+ ## Infrastructure
4
+
5
+ This project uses **jack** for deployment. When Jack MCP is connected, prefer `mcp__jack__*` tools over CLI commands - they're cloud-aware and faster.
6
+
7
+ **Do NOT use wrangler directly.** Jack manages config and may use cloud-hosted resources where wrangler won't work.
8
+
9
+ Common operations:
10
+ - **Deploy**: `jack ship` or Jack MCP
11
+ - **Database**: `jack services db create` or Jack MCP
12
+ - **Status**: `jack status` or Jack MCP
13
+
14
+ ## SQL Execution
15
+
16
+ Jack supports secure SQL execution against D1 databases:
17
+
18
+ **Via MCP** (preferred for agents):
19
+ - `execute_sql({ sql: "SELECT * FROM users" })` - read queries work by default
20
+ - `execute_sql({ sql: "INSERT...", allow_write: true })` - writes require allow_write
21
+ - Destructive ops (DROP, TRUNCATE, ALTER) are blocked via MCP - use CLI
22
+
23
+ **Via CLI**:
24
+ - `jack services db execute "SELECT * FROM users"` - read queries
25
+ - `jack services db execute "INSERT..." --write` - write queries
26
+ - `jack services db execute "DROP TABLE..." --write` - prompts for typed confirmation
27
+ - `jack services db execute --file schema.sql --write` - run SQL from file
28
+
29
+ **Security notes**:
30
+ - Read-only by default to prevent accidental data modification
31
+ - Write operations require explicit `--write` flag or `allow_write: true`
32
+ - Destructive operations (DROP, TRUNCATE, ALTER, DELETE without WHERE) require CLI confirmation
33
+ - MCP results are wrapped with anti-injection headers to prevent prompt injection
@@ -0,0 +1,33 @@
1
+ # Agent Context
2
+
3
+ ## Infrastructure
4
+
5
+ This project uses **jack** for deployment. When Jack MCP is connected, prefer `mcp__jack__*` tools over CLI commands - they're cloud-aware and faster.
6
+
7
+ **Do NOT use wrangler directly.** Jack manages config and may use cloud-hosted resources where wrangler won't work.
8
+
9
+ Common operations:
10
+ - **Deploy**: `jack ship` or Jack MCP
11
+ - **Database**: `jack services db create` or Jack MCP
12
+ - **Status**: `jack status` or Jack MCP
13
+
14
+ ## SQL Execution
15
+
16
+ Jack supports secure SQL execution against D1 databases:
17
+
18
+ **Via MCP** (preferred for agents):
19
+ - `execute_sql({ sql: "SELECT * FROM users" })` - read queries work by default
20
+ - `execute_sql({ sql: "INSERT...", allow_write: true })` - writes require allow_write
21
+ - Destructive ops (DROP, TRUNCATE, ALTER) are blocked via MCP - use CLI
22
+
23
+ **Via CLI**:
24
+ - `jack services db execute "SELECT * FROM users"` - read queries
25
+ - `jack services db execute "INSERT..." --write` - write queries
26
+ - `jack services db execute "DROP TABLE..." --write` - prompts for typed confirmation
27
+ - `jack services db execute --file schema.sql --write` - run SQL from file
28
+
29
+ **Security notes**:
30
+ - Read-only by default to prevent accidental data modification
31
+ - Write operations require explicit `--write` flag or `allow_write: true`
32
+ - Destructive operations (DROP, TRUNCATE, ALTER, DELETE without WHERE) require CLI confirmation
33
+ - MCP results are wrapped with anti-injection headers to prevent prompt injection