@everystack/mcp 0.3.2 → 0.3.3
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/dist/gates/detectors/embedded-data-bundle.d.ts +13 -0
- package/dist/gates/detectors/embedded-data-bundle.d.ts.map +1 -0
- package/dist/gates/detectors/embedded-data-bundle.js +56 -0
- package/dist/gates/detectors/hand-written-migration.d.ts +24 -0
- package/dist/gates/detectors/hand-written-migration.d.ts.map +1 -0
- package/dist/gates/detectors/hand-written-migration.js +54 -0
- package/dist/gates/detectors/secret-in-public-env.d.ts +12 -0
- package/dist/gates/detectors/secret-in-public-env.d.ts.map +1 -0
- package/dist/gates/detectors/secret-in-public-env.js +38 -0
- package/dist/gates/engine.d.ts +28 -0
- package/dist/gates/engine.d.ts.map +1 -0
- package/dist/gates/engine.js +74 -0
- package/dist/gates/registry.d.ts +14 -0
- package/dist/gates/registry.d.ts.map +1 -0
- package/dist/gates/registry.js +21 -0
- package/dist/gates/telemetry.d.ts +47 -0
- package/dist/gates/telemetry.d.ts.map +1 -0
- package/dist/gates/telemetry.js +121 -0
- package/dist/gates/types.d.ts +67 -0
- package/dist/gates/types.d.ts.map +1 -0
- package/dist/gates/types.js +14 -0
- package/dist/governance/cli.d.ts +57 -0
- package/dist/governance/cli.d.ts.map +1 -0
- package/dist/governance/cli.js +169 -0
- package/dist/governance/grounding.d.ts +78 -0
- package/dist/governance/grounding.d.ts.map +1 -0
- package/dist/governance/grounding.js +299 -0
- package/dist/index.cjs +11 -9
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +94 -0
- package/dist/project-claude-md.md +30 -16
- package/dist/prompts/add-feature.d.ts +3 -0
- package/dist/prompts/add-feature.d.ts.map +1 -0
- package/dist/prompts/add-feature.js +154 -0
- package/dist/prompts/claude-md.d.ts +12 -0
- package/dist/prompts/claude-md.d.ts.map +1 -0
- package/dist/prompts/claude-md.js +87 -0
- package/dist/prompts/debug.d.ts +3 -0
- package/dist/prompts/debug.d.ts.map +1 -0
- package/dist/prompts/debug.js +129 -0
- package/dist/prompts/deploy.d.ts +3 -0
- package/dist/prompts/deploy.d.ts.map +1 -0
- package/dist/prompts/deploy.js +118 -0
- package/dist/prompts/design-schema.d.ts +3 -0
- package/dist/prompts/design-schema.d.ts.map +1 -0
- package/dist/prompts/design-schema.js +97 -0
- package/dist/prompts/governance-setup.d.ts +9 -0
- package/dist/prompts/governance-setup.d.ts.map +1 -0
- package/dist/prompts/governance-setup.js +76 -0
- package/dist/prompts/index.d.ts +3 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +20 -0
- package/dist/prompts/new-app.d.ts +3 -0
- package/dist/prompts/new-app.d.ts.map +1 -0
- package/dist/prompts/new-app.js +203 -0
- package/dist/prompts/runbook.d.ts +12 -0
- package/dist/prompts/runbook.d.ts.map +1 -0
- package/dist/prompts/runbook.js +70 -0
- package/dist/prompts/secure.d.ts +3 -0
- package/dist/prompts/secure.d.ts.map +1 -0
- package/dist/prompts/secure.js +219 -0
- package/dist/resources/index.d.ts +12 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +182 -0
- package/dist/tools/check-environment.d.ts +25 -0
- package/dist/tools/check-environment.d.ts.map +1 -0
- package/dist/tools/check-environment.js +281 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +18 -0
- package/dist/tools/project-status.d.ts +28 -0
- package/dist/tools/project-status.d.ts.map +1 -0
- package/dist/tools/project-status.js +138 -0
- package/dist/tools/project-validate.d.ts +19 -0
- package/dist/tools/project-validate.d.ts.map +1 -0
- package/dist/tools/project-validate.js +323 -0
- package/dist/tools/schema-analyze.d.ts +46 -0
- package/dist/tools/schema-analyze.d.ts.map +1 -0
- package/dist/tools/schema-analyze.js +336 -0
- package/package.json +3 -3
- package/src/gates/detectors/hand-written-migration.ts +22 -13
- package/src/index.ts +1 -1
- package/src/prompts/claude-md.ts +4 -2
- package/src/resources/project-claude-md.md +30 -16
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerNewAppPrompt(server) {
|
|
3
|
+
server.prompt('new-app', 'Scaffold a new everystack app. Describe what you want to build and Claude will recommend the right tier, check prerequisites, and walk you through setup.', {
|
|
4
|
+
description: z.string().describe('Describe what you want to build (e.g., "a recipe sharing app where people can post and save recipes")'),
|
|
5
|
+
tier: z.enum(['V1', 'V2', 'V3']).optional().describe('Target tier: V1 (static), V2 (dynamic with DB), V3 (full platform). If omitted, recommended based on description.'),
|
|
6
|
+
name: z.string().optional().describe('App name (e.g., my-app). If omitted, suggested from description.'),
|
|
7
|
+
}, async ({ description, tier, name }) => {
|
|
8
|
+
const hasTier = tier !== undefined;
|
|
9
|
+
const hasName = name !== undefined;
|
|
10
|
+
const tierPackages = {
|
|
11
|
+
V1: ['@everystack/server', '@everystack/cli', '@everystack/ui'],
|
|
12
|
+
V2: [
|
|
13
|
+
'@everystack/server', '@everystack/cli', '@everystack/ui',
|
|
14
|
+
'@everystack/api', '@everystack/auth', '@everystack/admin',
|
|
15
|
+
'@everystack/logging', '@everystack/security', '@everystack/query',
|
|
16
|
+
],
|
|
17
|
+
V3: [
|
|
18
|
+
'@everystack/server', '@everystack/cli', '@everystack/ui',
|
|
19
|
+
'@everystack/api', '@everystack/auth', '@everystack/admin',
|
|
20
|
+
'@everystack/logging', '@everystack/security', '@everystack/query',
|
|
21
|
+
'@everystack/jobs', '@everystack/storage', '@everystack/images',
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
// When tier is omitted, build an intake phase for Claude to work through
|
|
25
|
+
const intakeSection = !hasTier ? [
|
|
26
|
+
'## Step 1: Understand the Idea',
|
|
27
|
+
'',
|
|
28
|
+
`The user wants to build: "${description}"`,
|
|
29
|
+
'',
|
|
30
|
+
'Read the everystack://getting-started resource for how to explain things to beginners.',
|
|
31
|
+
'Listen to what they want to build. Do NOT categorize into tiers or ask them to choose.',
|
|
32
|
+
'Every app starts the same way: a running Expo app. Build the UI first, add complexity when features demand it.',
|
|
33
|
+
'',
|
|
34
|
+
'## Step 2: Choose a Name',
|
|
35
|
+
'',
|
|
36
|
+
hasName
|
|
37
|
+
? `Use the name: "${name}"`
|
|
38
|
+
: 'Suggest a short, lowercase, hyphenated name based on the description (e.g., "recipe-share", "my-recipes"). Ask the user if they like it or want something different.',
|
|
39
|
+
'',
|
|
40
|
+
'## Step 3: Check Prerequisites',
|
|
41
|
+
'',
|
|
42
|
+
'Run the check_environment tool with phase "local".',
|
|
43
|
+
'Read everystack://getting-started for how to explain each prerequisite to a beginner.',
|
|
44
|
+
'Walk through installing missing tools ONE AT A TIME. Confirm each works before moving to the next.',
|
|
45
|
+
'Only V1 prerequisites are needed now — Node.js, git, pnpm. PostgreSQL is NOT needed yet.',
|
|
46
|
+
'AWS tools (AWS CLI, credentials, SST) are NOT needed now. The user will build and run locally first.',
|
|
47
|
+
'',
|
|
48
|
+
'## Step 4: Scaffold the Project',
|
|
49
|
+
'',
|
|
50
|
+
'Once prerequisites are ready and the user has confirmed the name, proceed with the scaffolding steps below.',
|
|
51
|
+
'The app starts as a static Expo app. Database, auth, and other features are added later when needed.',
|
|
52
|
+
'Explain each step in plain language alongside the technical commands.',
|
|
53
|
+
'',
|
|
54
|
+
].join('\n') : '';
|
|
55
|
+
const effectiveTier = tier || 'V1';
|
|
56
|
+
const effectiveName = name || 'my-app';
|
|
57
|
+
const packages = tierPackages[effectiveTier];
|
|
58
|
+
return {
|
|
59
|
+
messages: [
|
|
60
|
+
{
|
|
61
|
+
role: 'user',
|
|
62
|
+
content: {
|
|
63
|
+
type: 'text',
|
|
64
|
+
text: [
|
|
65
|
+
intakeSection,
|
|
66
|
+
hasTier
|
|
67
|
+
? `Create a new everystack ${effectiveTier} app called "${effectiveName}".`
|
|
68
|
+
: '## Scaffolding Instructions',
|
|
69
|
+
`Description: ${description}`,
|
|
70
|
+
'',
|
|
71
|
+
hasTier ? [
|
|
72
|
+
'## Prerequisites',
|
|
73
|
+
'',
|
|
74
|
+
'Run the check_environment tool with phase "local" to verify local development prerequisites.',
|
|
75
|
+
'If any required tools are missing, help the user install them before proceeding.',
|
|
76
|
+
'AWS tools are not needed yet — the user will build and run locally first.',
|
|
77
|
+
].join('\n') : '',
|
|
78
|
+
'',
|
|
79
|
+
'## Instructions',
|
|
80
|
+
'',
|
|
81
|
+
'1. Read the everystack://core resource for architecture and conventions.',
|
|
82
|
+
effectiveTier !== 'V1' ? '2. Read everystack://security for the security model.' : '',
|
|
83
|
+
effectiveTier !== 'V1' ? '3. Read everystack://schema-patterns for Drizzle schema design.' : '',
|
|
84
|
+
effectiveTier !== 'V1' ? '4. Read everystack://auth for authentication setup.' : '',
|
|
85
|
+
'',
|
|
86
|
+
'## Steps to Execute',
|
|
87
|
+
'',
|
|
88
|
+
'### 1. Project Setup',
|
|
89
|
+
`- Create the project directory: ${effectiveName}/`,
|
|
90
|
+
'- Initialize with: `npx create-expo-app@latest`',
|
|
91
|
+
`- Install packages: \`pnpm add ${packages.join(' ')}\``,
|
|
92
|
+
effectiveTier !== 'V1' ? '- Install peer deps: `pnpm add drizzle-orm && pnpm add -D drizzle-kit`' : '',
|
|
93
|
+
'- Install SST: `pnpm add -D sst`',
|
|
94
|
+
'',
|
|
95
|
+
'### 2. Project Structure',
|
|
96
|
+
'```',
|
|
97
|
+
`${effectiveName}/`,
|
|
98
|
+
'├── app/ # Expo Router pages',
|
|
99
|
+
effectiveTier !== 'V1' ? '├── db/ # Schema, migrations, seed' : '',
|
|
100
|
+
'├── server/ # Lambda handlers',
|
|
101
|
+
effectiveTier !== 'V1' ? '│ └── api.ts # PostgREST handler' : '',
|
|
102
|
+
effectiveTier === 'V3' ? '│ ├── worker.ts # SQS worker handler' : '',
|
|
103
|
+
effectiveTier === 'V3' ? '│ └── image.ts # Image processing handler' : '',
|
|
104
|
+
'├── lib/ # Shared code (auth context, API client)',
|
|
105
|
+
'├── sst.config.ts # Infrastructure',
|
|
106
|
+
'└── package.json',
|
|
107
|
+
'```',
|
|
108
|
+
'',
|
|
109
|
+
effectiveTier !== 'V1' ? [
|
|
110
|
+
'### 3. Database Schema',
|
|
111
|
+
`Create db/schema.ts with tables for the ${description}.`,
|
|
112
|
+
'Follow the patterns from everystack://schema-patterns:',
|
|
113
|
+
'- UUID primary keys with defaultRandom()',
|
|
114
|
+
'- created_at timestamps on all tables',
|
|
115
|
+
'- Foreign key references with proper cascading',
|
|
116
|
+
'- Relations for both SSR and API query embedding',
|
|
117
|
+
'',
|
|
118
|
+
'### 4. Migrations',
|
|
119
|
+
'- Generate: `npx drizzle-kit generate`',
|
|
120
|
+
'- Add RLS policies in a custom SQL migration',
|
|
121
|
+
'- Reference the RLS templates from everystack://security',
|
|
122
|
+
'',
|
|
123
|
+
'### 5. Handler Configuration',
|
|
124
|
+
'Create server/api.ts using createPluginLambdaHandler or createLambdaHandler.',
|
|
125
|
+
'Include:',
|
|
126
|
+
'- auth.verifyToken for JWT verification',
|
|
127
|
+
'- pgSettings for RLS context injection',
|
|
128
|
+
'- exposedTables to limit API surface',
|
|
129
|
+
'- rowOwnership for user-scoped mutation control',
|
|
130
|
+
'- softDelete for reversible deletes',
|
|
131
|
+
'',
|
|
132
|
+
'### 6. Auth Setup',
|
|
133
|
+
'Read everystack://auth for the full auth flow.',
|
|
134
|
+
'- Set up createAuthHandlers with the JWT secret',
|
|
135
|
+
'- Create lib/auth-context.tsx with AuthProvider',
|
|
136
|
+
'- Add signup/signin screens',
|
|
137
|
+
'',
|
|
138
|
+
].join('\n') : '',
|
|
139
|
+
'## Validation',
|
|
140
|
+
'',
|
|
141
|
+
'After scaffolding, run the project_validate tool to check for common mistakes.',
|
|
142
|
+
'',
|
|
143
|
+
'## Run Locally',
|
|
144
|
+
'',
|
|
145
|
+
'Read everystack://first-run for a walkthrough of what to expect.',
|
|
146
|
+
'- Run `npx expo start` (or `pnpm dev`)',
|
|
147
|
+
'- Press `w` to open in a browser',
|
|
148
|
+
'- Verify the app loads and displays correctly',
|
|
149
|
+
'- Try changing text in app/index.tsx to confirm hot reload works',
|
|
150
|
+
'',
|
|
151
|
+
'This is the milestone: the user sees their app running on their own computer.',
|
|
152
|
+
'Celebrate this. Then continue building features.',
|
|
153
|
+
'',
|
|
154
|
+
!hasTier ? [
|
|
155
|
+
'## Growing Your App',
|
|
156
|
+
'',
|
|
157
|
+
'Build the UI together first — screens, navigation, components, styling. Everything is static and visual.',
|
|
158
|
+
'',
|
|
159
|
+
'When a feature needs user accounts, login, or saved data:',
|
|
160
|
+
'- Read everystack://adding-database for the step-by-step guide',
|
|
161
|
+
'- Run check_environment("local") again — PostgreSQL will be needed',
|
|
162
|
+
'',
|
|
163
|
+
'When a feature needs file uploads or background processing:',
|
|
164
|
+
'- Read everystack://storage for file uploads',
|
|
165
|
+
'- Read everystack://jobs for background tasks',
|
|
166
|
+
'- Read everystack://images for image processing',
|
|
167
|
+
].join('\n') : '',
|
|
168
|
+
'',
|
|
169
|
+
'## Project Documentation',
|
|
170
|
+
'Scaffold the project contract (use the claude_md prompt): read the everystack://project-claude-md',
|
|
171
|
+
'resource and write a CLAUDE.md to the project root. This file activates the grounding gate —',
|
|
172
|
+
'every later session must read it before editing — so it carries the non-negotiables the cheat',
|
|
173
|
+
'gates enforce. Keep it current as the project grows (claude_md reconciles it).',
|
|
174
|
+
`Replace {PROJECT_NAME} with "${effectiveName}", {ONE_LINE_DESCRIPTION} with "${description}",`,
|
|
175
|
+
'and {ANNOTATED_DIRECTORY_TREE} with the actual project structure created above.',
|
|
176
|
+
'',
|
|
177
|
+
'## Deployment (Later)',
|
|
178
|
+
'',
|
|
179
|
+
'When the user is ready to put the app on the internet:',
|
|
180
|
+
'1. Run check_environment with phase "deploy" to verify AWS prerequisites',
|
|
181
|
+
'2. Set up AWS credentials (read everystack://aws-setup for beginners, everystack://security for IAM profiles)',
|
|
182
|
+
'### SST Configuration',
|
|
183
|
+
'Create sst.config.ts with:',
|
|
184
|
+
'- S3 bucket for static assets',
|
|
185
|
+
'- CloudFront distribution',
|
|
186
|
+
'- Lambda for SSR',
|
|
187
|
+
effectiveTier !== 'V1' ? '- RDS Aurora Serverless PostgreSQL' : '',
|
|
188
|
+
effectiveTier !== 'V1' ? '- VPC configuration' : '',
|
|
189
|
+
effectiveTier === 'V3' ? '- SQS queue for background jobs' : '',
|
|
190
|
+
effectiveTier === 'V3' ? '- S3 bucket for file uploads' : '',
|
|
191
|
+
'',
|
|
192
|
+
'### Deploy',
|
|
193
|
+
'Read everystack://deployment for the full walkthrough.',
|
|
194
|
+
'1. `pnpm sst deploy --stage dev`',
|
|
195
|
+
effectiveTier !== 'V1' ? '2. `everystack db:migrate`' : '',
|
|
196
|
+
effectiveTier !== 'V1' ? '3. `everystack db:seed` (dev only)' : '',
|
|
197
|
+
].filter(Boolean).join('\n'),
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
/**
|
|
3
|
+
* `runbook` — generate or refresh docs/RUNBOOK.md, the per-app operations manual.
|
|
4
|
+
*
|
|
5
|
+
* The compiler lives in the CLI (`everystack runbook` — db:generate for docs:
|
|
6
|
+
* detection → sections keyed by the app's reality → es:gen/es:slot merge). This
|
|
7
|
+
* prompt is the thin agent layer on top: run the compiler, then fill the EMPTY
|
|
8
|
+
* slots with app-specific narrative the compiler cannot know. Generated blocks
|
|
9
|
+
* belong to the compiler — the agent never writes inside them.
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerRunbookPrompt(server: McpServer): void;
|
|
12
|
+
//# sourceMappingURL=runbook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runbook.d.ts","sourceRoot":"","sources":["../../src/prompts/runbook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgE7D"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* `runbook` — generate or refresh docs/RUNBOOK.md, the per-app operations manual.
|
|
4
|
+
*
|
|
5
|
+
* The compiler lives in the CLI (`everystack runbook` — db:generate for docs:
|
|
6
|
+
* detection → sections keyed by the app's reality → es:gen/es:slot merge). This
|
|
7
|
+
* prompt is the thin agent layer on top: run the compiler, then fill the EMPTY
|
|
8
|
+
* slots with app-specific narrative the compiler cannot know. Generated blocks
|
|
9
|
+
* belong to the compiler — the agent never writes inside them.
|
|
10
|
+
*/
|
|
11
|
+
export function registerRunbookPrompt(server) {
|
|
12
|
+
server.prompt('runbook', 'Generate or refresh docs/RUNBOOK.md — the operations manual compiled from the app\'s detected reality (tier, Models, handlers). Runs `everystack runbook`, then fills empty slots with app narrative (confirm-first). Regenerate after the app grows or an everystack upgrade.', {
|
|
13
|
+
projectPath: z.string().optional().describe('Absolute path to the project root (where docs/RUNBOOK.md lives)'),
|
|
14
|
+
}, async ({ projectPath }) => {
|
|
15
|
+
const root = projectPath ?? '.';
|
|
16
|
+
return {
|
|
17
|
+
messages: [
|
|
18
|
+
{
|
|
19
|
+
role: 'user',
|
|
20
|
+
content: {
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: [
|
|
23
|
+
`Generate or refresh docs/RUNBOOK.md for the project at ${root}.`,
|
|
24
|
+
'',
|
|
25
|
+
'## What this is',
|
|
26
|
+
'',
|
|
27
|
+
'docs/RUNBOOK.md is the per-app operations manual — how to configure, extend, deploy,',
|
|
28
|
+
'monitor, and maintain THIS app. It is COMPILED, not authored: `everystack runbook`',
|
|
29
|
+
'derives it from the app\'s detected reality (installed packages → tier, Models,',
|
|
30
|
+
'handlers, crons) and regenerating after a change diffs in what\'s new. The division',
|
|
31
|
+
'of labor: CLAUDE.md is the terse contract (rules); the runbook is procedures.',
|
|
32
|
+
'',
|
|
33
|
+
'## Step 1 — run the compiler',
|
|
34
|
+
'',
|
|
35
|
+
`- If ${root}/docs/RUNBOOK.md exists, run \`everystack runbook --diff\` first and show`,
|
|
36
|
+
' the human which sections would change.',
|
|
37
|
+
'- Then run `everystack runbook` to write it. If it reports hand-edited generated',
|
|
38
|
+
' sections (a conflict), do NOT reach for --force — help the human move those words',
|
|
39
|
+
' into a slot or the Notes section first, then regenerate. --force only with their',
|
|
40
|
+
' explicit ok.',
|
|
41
|
+
'',
|
|
42
|
+
'## Step 2 — fill the EMPTY slots (and only the slots)',
|
|
43
|
+
'',
|
|
44
|
+
'The document has two kinds of content, mechanically marked:',
|
|
45
|
+
'- `<!-- es:gen section=... -->` blocks belong to the compiler. NEVER write inside one —',
|
|
46
|
+
' your words would be overwritten on the next regeneration, and the hash check flags it.',
|
|
47
|
+
'- `<!-- es:slot name=... -->` blocks are the human/agent seam and survive every',
|
|
48
|
+
' regeneration. A slot still holding its `_(Fill in: ...)_` placeholder is empty.',
|
|
49
|
+
'',
|
|
50
|
+
'For each empty slot, draft content from what you know of the project:',
|
|
51
|
+
'- `overview` — what the app is, who it serves, what an operator should know first.',
|
|
52
|
+
'- `stage-notes` — deployed URLs per stage, channel-to-stage mapping, release cadence',
|
|
53
|
+
' (read `.sst/outputs.json` or ask; do not guess URLs).',
|
|
54
|
+
'- `notes` — anything project-specific that fits nowhere else. Leave it if nothing real.',
|
|
55
|
+
'',
|
|
56
|
+
'Show the human your draft slot content and let them approve before writing — the',
|
|
57
|
+
'runbook is theirs; you assist, you do not own it.',
|
|
58
|
+
'',
|
|
59
|
+
'## Step 3 — verify',
|
|
60
|
+
'',
|
|
61
|
+
'Run `everystack runbook --check`: it must exit clean (slot edits never make it stale).',
|
|
62
|
+
'Tell the human: regenerate any time with `everystack runbook`; CI can enforce currency',
|
|
63
|
+
'with `--check`; after an everystack upgrade the diff reads as release notes for this app.',
|
|
64
|
+
].join('\n'),
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secure.d.ts","sourceRoot":"","sources":["../../src/prompts/secure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmO5D"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerSecurePrompt(server) {
|
|
3
|
+
server.prompt('secure', 'Set up security for an everystack project: AWS IAM profiles, JWT auth, RLS policies, and edge verification.', {
|
|
4
|
+
features: z.string().optional().describe('Comma-separated security features to set up (e.g., "aws,rls,auth"). Defaults to all.'),
|
|
5
|
+
projectPath: z.string().optional().describe('Absolute path to project root'),
|
|
6
|
+
}, async ({ features, projectPath }) => {
|
|
7
|
+
const allFeatures = ['aws', 'rls', 'auth', 'edge'];
|
|
8
|
+
const requested = features
|
|
9
|
+
? features.split(',').map((f) => f.trim().toLowerCase())
|
|
10
|
+
: allFeatures;
|
|
11
|
+
return {
|
|
12
|
+
messages: [
|
|
13
|
+
{
|
|
14
|
+
role: 'user',
|
|
15
|
+
content: {
|
|
16
|
+
type: 'text',
|
|
17
|
+
text: [
|
|
18
|
+
`Set up security for ${projectPath ? 'the project at ' + projectPath : 'my everystack project'}.`,
|
|
19
|
+
`Features: ${requested.join(', ')}`,
|
|
20
|
+
'',
|
|
21
|
+
'## Instructions',
|
|
22
|
+
'',
|
|
23
|
+
'1. Read everystack://security for the complete security model.',
|
|
24
|
+
'2. Read everystack://auth for JWT authentication details.',
|
|
25
|
+
projectPath ? `3. Run project_validate with projectPath="${projectPath}" to find existing security gaps.` : '',
|
|
26
|
+
projectPath ? `4. Run schema_analyze with projectPath="${projectPath}" to check schema security.` : '',
|
|
27
|
+
'',
|
|
28
|
+
requested.includes('aws') ? [
|
|
29
|
+
'## AWS IAM Profiles',
|
|
30
|
+
'',
|
|
31
|
+
'Set up three named AWS profiles with least-privilege permissions:',
|
|
32
|
+
'',
|
|
33
|
+
'### 1. everystack-create (Infrastructure Creation)',
|
|
34
|
+
'',
|
|
35
|
+
'**When:** First deploy only. Disable after infrastructure exists.',
|
|
36
|
+
'',
|
|
37
|
+
'```ini',
|
|
38
|
+
'# ~/.aws/credentials',
|
|
39
|
+
'[everystack-create]',
|
|
40
|
+
'aws_access_key_id = ...',
|
|
41
|
+
'aws_secret_access_key = ...',
|
|
42
|
+
'```',
|
|
43
|
+
'',
|
|
44
|
+
'Permissions needed:',
|
|
45
|
+
'- CloudFormation (create stacks)',
|
|
46
|
+
'- S3 (create buckets)',
|
|
47
|
+
'- CloudFront (create distributions)',
|
|
48
|
+
'- Lambda (create functions)',
|
|
49
|
+
'- RDS (create clusters)',
|
|
50
|
+
'- IAM (create roles — scoped to everystack-* prefix)',
|
|
51
|
+
'- VPC (create VPC, subnets, security groups)',
|
|
52
|
+
'- SQS (create queues)',
|
|
53
|
+
'',
|
|
54
|
+
'**After first deploy:** Delete or deactivate these access keys.',
|
|
55
|
+
'',
|
|
56
|
+
'### 2. everystack-manage (Day-to-Day CLI)',
|
|
57
|
+
'',
|
|
58
|
+
'**When:** Development, running CLI commands, migrations.',
|
|
59
|
+
'',
|
|
60
|
+
'Permissions needed:',
|
|
61
|
+
'- Lambda:InvokeFunction (for CLI → Lambda operations)',
|
|
62
|
+
'- S3:PutObject/GetObject (for OTA updates)',
|
|
63
|
+
'- CloudWatch:GetLogEvents (for log tailing)',
|
|
64
|
+
'- SSM:GetParameter (for secret access)',
|
|
65
|
+
'',
|
|
66
|
+
'**Cannot:** Create or delete infrastructure, modify IAM, access RDS directly.',
|
|
67
|
+
'',
|
|
68
|
+
'### 3. everystack-deploy (CI/CD)',
|
|
69
|
+
'',
|
|
70
|
+
'**When:** Automated deployments from CI/CD pipelines.',
|
|
71
|
+
'',
|
|
72
|
+
'Permissions needed:',
|
|
73
|
+
'- CloudFormation:UpdateStack (update existing, not create new)',
|
|
74
|
+
'- Lambda:UpdateFunctionCode',
|
|
75
|
+
'- S3:PutObject (deploy assets)',
|
|
76
|
+
'- CloudFront:CreateInvalidation',
|
|
77
|
+
'',
|
|
78
|
+
'**Cannot:** Create new infrastructure, modify IAM, access secrets.',
|
|
79
|
+
'',
|
|
80
|
+
].join('\n') : '',
|
|
81
|
+
requested.includes('auth') ? [
|
|
82
|
+
'## JWT Authentication',
|
|
83
|
+
'',
|
|
84
|
+
'Read everystack://auth for full details.',
|
|
85
|
+
'',
|
|
86
|
+
'### Setup',
|
|
87
|
+
'',
|
|
88
|
+
'1. Generate a strong JWT secret:',
|
|
89
|
+
'```bash',
|
|
90
|
+
'pnpm sst secret set JwtSecret "$(openssl rand -base64 32)" --stage dev',
|
|
91
|
+
'```',
|
|
92
|
+
'',
|
|
93
|
+
'2. Create auth handlers:',
|
|
94
|
+
'```typescript',
|
|
95
|
+
"import { createAuthHandlers } from '@everystack/auth';",
|
|
96
|
+
'',
|
|
97
|
+
'const auth = createAuthHandlers(db, schema, jwtSecret, {',
|
|
98
|
+
" mode: 'postgres',",
|
|
99
|
+
' sql,',
|
|
100
|
+
' cookies: { allowedOrigins: ["https://your-web-origin"] }, // HttpOnly cookies for web — Origin allowlist required (fail-closed)',
|
|
101
|
+
'});',
|
|
102
|
+
'```',
|
|
103
|
+
'',
|
|
104
|
+
'3. Wire into Lambda handler with auth routes.',
|
|
105
|
+
'',
|
|
106
|
+
'4. Create AuthProvider in lib/auth-context.tsx.',
|
|
107
|
+
'',
|
|
108
|
+
'### Token Lifecycle',
|
|
109
|
+
'',
|
|
110
|
+
'- Access tokens: short-lived (15 min default)',
|
|
111
|
+
'- Refresh tokens: long-lived (7 days default)',
|
|
112
|
+
'- Auto-refresh on 401 via client interceptor',
|
|
113
|
+
'',
|
|
114
|
+
].join('\n') : '',
|
|
115
|
+
requested.includes('rls') ? [
|
|
116
|
+
'## Row-Level Security (RLS)',
|
|
117
|
+
'',
|
|
118
|
+
'Read everystack://security for the mental model and templates.',
|
|
119
|
+
'',
|
|
120
|
+
'### How RLS Works in everystack',
|
|
121
|
+
'',
|
|
122
|
+
'1. Client sends JWT in Authorization header',
|
|
123
|
+
'2. Handler verifies JWT, extracts claims',
|
|
124
|
+
'3. `pgSettings` sets PostgreSQL session variables:',
|
|
125
|
+
' - `SET LOCAL ROLE authenticated`',
|
|
126
|
+
' - `set_config(\'request.jwt.claims\', \'{"sub":"...","role":"..."}\', true)`',
|
|
127
|
+
'4. Query runs inside a transaction with those settings',
|
|
128
|
+
'5. RLS policies read the claims and filter rows',
|
|
129
|
+
'',
|
|
130
|
+
'### Step-by-Step',
|
|
131
|
+
'',
|
|
132
|
+
'1. **Create roles** (one-time migration):',
|
|
133
|
+
'```sql',
|
|
134
|
+
'CREATE ROLE anon NOLOGIN;',
|
|
135
|
+
'CREATE ROLE authenticated NOLOGIN;',
|
|
136
|
+
'CREATE ROLE admin NOLOGIN;',
|
|
137
|
+
'GRANT anon, authenticated, admin TO authenticator;',
|
|
138
|
+
'```',
|
|
139
|
+
'',
|
|
140
|
+
'2. **Enable RLS on every table:**',
|
|
141
|
+
'```sql',
|
|
142
|
+
'ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;',
|
|
143
|
+
'```',
|
|
144
|
+
'',
|
|
145
|
+
'3. **Grant minimum permissions:**',
|
|
146
|
+
'```sql',
|
|
147
|
+
'GRANT SELECT ON table_name TO anon;',
|
|
148
|
+
'GRANT SELECT, INSERT, UPDATE, DELETE ON table_name TO authenticated;',
|
|
149
|
+
'GRANT ALL ON table_name TO admin;',
|
|
150
|
+
'```',
|
|
151
|
+
'',
|
|
152
|
+
'4. **Create policies per access pattern:**',
|
|
153
|
+
'',
|
|
154
|
+
'**Public read (soft-delete aware):**',
|
|
155
|
+
'```sql',
|
|
156
|
+
"CREATE POLICY \"anon_select\" ON table_name FOR SELECT TO anon",
|
|
157
|
+
' USING (deleted_at IS NULL);',
|
|
158
|
+
'```',
|
|
159
|
+
'',
|
|
160
|
+
'**Own rows only:**',
|
|
161
|
+
'```sql',
|
|
162
|
+
"CREATE POLICY \"own_rows_select\" ON table_name FOR SELECT TO authenticated",
|
|
163
|
+
" USING (user_id = (current_setting('request.jwt.claims', true)::json->>'sub')::uuid);",
|
|
164
|
+
'```',
|
|
165
|
+
'',
|
|
166
|
+
'**Admin full access:**',
|
|
167
|
+
'```sql',
|
|
168
|
+
"CREATE POLICY \"admin_all\" ON table_name FOR ALL TO admin USING (true);",
|
|
169
|
+
'```',
|
|
170
|
+
'',
|
|
171
|
+
'5. **Configure pgSettings in handler:**',
|
|
172
|
+
'```typescript',
|
|
173
|
+
'pgSettings: (user) => ({',
|
|
174
|
+
" role: user?.role === 'admin' ? 'admin' : user ? 'authenticated' : 'anon',",
|
|
175
|
+
" 'request.jwt.claims': JSON.stringify(user || { role: 'anon' }),",
|
|
176
|
+
'}),',
|
|
177
|
+
'```',
|
|
178
|
+
'',
|
|
179
|
+
'### Testing RLS',
|
|
180
|
+
'',
|
|
181
|
+
'```sql',
|
|
182
|
+
'-- Test as authenticated user',
|
|
183
|
+
'BEGIN;',
|
|
184
|
+
'SET LOCAL ROLE authenticated;',
|
|
185
|
+
"SELECT set_config('request.jwt.claims',",
|
|
186
|
+
" '{\"sub\": \"user-uuid\", \"role\": \"authenticated\"}', true);",
|
|
187
|
+
'SELECT * FROM posts; -- should only return own posts',
|
|
188
|
+
'ROLLBACK;',
|
|
189
|
+
'```',
|
|
190
|
+
'',
|
|
191
|
+
].join('\n') : '',
|
|
192
|
+
requested.includes('edge') ? [
|
|
193
|
+
'## Edge JWT Verification',
|
|
194
|
+
'',
|
|
195
|
+
'Verify JWTs at the CDN edge before requests reach Lambda:',
|
|
196
|
+
'',
|
|
197
|
+
'- CloudFront function validates JWT signature (HS256)',
|
|
198
|
+
'- Rejects expired or malformed tokens at the edge',
|
|
199
|
+
'- Reduces Lambda invocations for unauthorized requests',
|
|
200
|
+
'- See everystack://deployment for CloudFront function setup',
|
|
201
|
+
'',
|
|
202
|
+
].join('\n') : '',
|
|
203
|
+
'## Verification',
|
|
204
|
+
'',
|
|
205
|
+
'After setup, verify security:',
|
|
206
|
+
'',
|
|
207
|
+
'1. Run project_validate to check for gaps',
|
|
208
|
+
'2. Test unauthenticated access returns 401',
|
|
209
|
+
'3. Test cross-user data isolation with RLS',
|
|
210
|
+
'4. Verify JWT claims propagate through pgSettings',
|
|
211
|
+
'5. Check `.env` files are not committed',
|
|
212
|
+
'6. Confirm `everystack-create` profile is disabled',
|
|
213
|
+
].filter(Boolean).join('\n'),
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
};
|
|
218
|
+
});
|
|
219
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
declare function loadResource(filename: string): string;
|
|
3
|
+
interface ResourceDef {
|
|
4
|
+
uri: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
filename: string;
|
|
8
|
+
}
|
|
9
|
+
declare const RESOURCES: ResourceDef[];
|
|
10
|
+
export declare function registerResources(server: McpServer): void;
|
|
11
|
+
export { RESOURCES, loadResource };
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmBzE,iBAAS,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,UAAU,WAAW;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,QAAA,MAAM,SAAS,EAAE,WAAW,EAoL3B,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAOzD;AAED,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC"}
|