@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.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/commands/clone.ts +62 -40
- package/src/commands/community.ts +47 -0
- package/src/commands/init.ts +6 -0
- package/src/commands/services.ts +354 -9
- package/src/commands/sync.ts +9 -0
- package/src/commands/update.ts +10 -0
- package/src/index.ts +7 -0
- package/src/lib/control-plane.ts +62 -0
- package/src/lib/hooks.ts +20 -0
- package/src/lib/managed-deploy.ts +26 -2
- package/src/lib/output.ts +21 -3
- package/src/lib/progress.ts +160 -0
- package/src/lib/project-operations.ts +381 -93
- package/src/lib/services/db-create.ts +6 -3
- package/src/lib/services/db-execute.ts +485 -0
- package/src/lib/services/sql-classifier.test.ts +404 -0
- package/src/lib/services/sql-classifier.ts +346 -0
- package/src/lib/storage/file-filter.ts +4 -0
- package/src/lib/telemetry.ts +3 -0
- package/src/lib/version-check.ts +14 -0
- package/src/lib/wrangler-config.test.ts +322 -0
- package/src/lib/wrangler-config.ts +649 -0
- package/src/lib/zip-packager.ts +38 -0
- package/src/lib/zip-utils.ts +38 -0
- package/src/mcp/tools/index.ts +161 -0
- package/src/templates/index.ts +4 -0
- package/src/templates/types.ts +12 -0
- package/templates/api/AGENTS.md +33 -0
- package/templates/hello/AGENTS.md +33 -0
- package/templates/miniapp/.jack.json +4 -5
- package/templates/miniapp/AGENTS.md +33 -0
- package/templates/nextjs/AGENTS.md +33 -0
|
@@ -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
|
+
}
|
package/src/mcp/tools/index.ts
CHANGED
|
@@ -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
|
}
|
package/src/templates/index.ts
CHANGED
|
@@ -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"],
|
package/src/templates/types.ts
CHANGED
|
@@ -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
|
-
"
|
|
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
|