@everystack/mcp 0.2.0 → 0.2.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@everystack/mcp",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "MCP server that makes Claude an everystack expert",
5
5
  "license": "AGPL-3.0-only",
6
6
  "publishConfig": {
@@ -58,12 +58,7 @@ export function registerDeployPrompt(server: McpServer): void {
58
58
  ].join('\n') : '',
59
59
  '## Deploy Steps',
60
60
  '',
61
- '### 1. Build',
62
- '```bash',
63
- 'pnpm export # Expo export (builds dist/)',
64
- '```',
65
- '',
66
- '### 2. Deploy Infrastructure',
61
+ '### 1. Deploy Infrastructure',
67
62
  '```bash',
68
63
  `pnpm sst deploy --stage ${stage}`,
69
64
  '```',
@@ -105,7 +105,7 @@ export function registerSecurePrompt(server: McpServer): void {
105
105
  'const auth = createAuthHandlers(db, schema, jwtSecret, {',
106
106
  " mode: 'postgres',",
107
107
  ' sql,',
108
- ' cookies: true, // HttpOnly cookies for web',
108
+ ' cookies: { allowedOrigins: ["https://your-web-origin"] }, // HttpOnly cookies for web — Origin allowlist required (fail-closed)',
109
109
  '});',
110
110
  '```',
111
111
  '',
@@ -46,69 +46,152 @@ Your AWS account has a "root user" (the email you signed up with). The root user
46
46
 
47
47
  ### Create the permissions policy
48
48
 
49
- This policy gives the user exactly the permissions SST needs to deploy your app.
49
+ This policy gives the user the permissions SST needs to deploy your app, scoped to your app's resources. Every statement targets your app name prefix so this user can't touch other apps in the same account.
50
50
 
51
51
  7. Select **Attach policies directly**
52
52
  8. Click **Create policy** (this opens a new tab)
53
53
  9. Click the **JSON** tab
54
- 10. Delete anything in the editor and paste this:
54
+ 10. Delete anything in the editor and paste this (replacing the placeholders — see table below):
55
55
 
56
56
  ```json
57
57
  {
58
58
  "Version": "2012-10-17",
59
59
  "Statement": [
60
60
  {
61
- "Sid": "EverystackDeploy",
61
+ "Sid": "SSM",
62
+ "Effect": "Allow",
63
+ "Action": "ssm:*",
64
+ "Resource": [
65
+ "arn:aws:ssm:{REGION}:{ACCOUNT_ID}:parameter/sst/*",
66
+ "arn:aws:ssm:{REGION}:{ACCOUNT_ID}:parameter/{APP_NAME}/*"
67
+ ]
68
+ },
69
+ {
70
+ "Sid": "S3",
71
+ "Effect": "Allow",
72
+ "Action": "s3:*",
73
+ "Resource": [
74
+ "arn:aws:s3:::{APP_NAME}-*-*",
75
+ "arn:aws:s3:::{APP_NAME}-*-*/*",
76
+ "arn:aws:s3:::sst-asset-*",
77
+ "arn:aws:s3:::sst-asset-*/*",
78
+ "arn:aws:s3:::sst-state-*",
79
+ "arn:aws:s3:::sst-state-*/*"
80
+ ]
81
+ },
82
+ {
83
+ "Sid": "S3Protect",
84
+ "Effect": "Deny",
85
+ "Action": [
86
+ "s3:DeleteBucket",
87
+ "s3:PutBucketAcl",
88
+ "s3:PutBucketVersioning",
89
+ "s3:PutBucketEncryption",
90
+ "s3:PutBucketLogging"
91
+ ],
92
+ "Resource": "*"
93
+ },
94
+ {
95
+ "Sid": "CF",
62
96
  "Effect": "Allow",
63
97
  "Action": [
64
- "cloudformation:*",
65
- "s3:*",
66
- "lambda:*",
67
98
  "cloudfront:*",
68
- "iam:CreateRole",
69
- "iam:DeleteRole",
70
- "iam:GetRole",
71
- "iam:GetRolePolicy",
72
- "iam:PassRole",
73
- "iam:AttachRolePolicy",
74
- "iam:DetachRolePolicy",
75
- "iam:PutRolePolicy",
76
- "iam:DeleteRolePolicy",
77
- "iam:ListRolePolicies",
78
- "iam:ListAttachedRolePolicies",
79
- "iam:TagRole",
80
- "iam:UntagRole",
81
- "iam:CreateServiceLinkedRole",
82
- "rds:*",
83
- "ec2:DescribeVpcs",
84
- "ec2:DescribeSubnets",
85
- "ec2:DescribeSecurityGroups",
86
- "ec2:DescribeRouteTables",
87
- "ec2:DescribeAvailabilityZones",
88
- "ec2:DescribeVpcEndpoints",
89
- "ec2:CreateSecurityGroup",
90
- "ec2:DeleteSecurityGroup",
91
- "ec2:AuthorizeSecurityGroupIngress",
92
- "ec2:RevokeSecurityGroupIngress",
93
- "ec2:AuthorizeSecurityGroupEgress",
94
- "ec2:RevokeSecurityGroupEgress",
95
- "ec2:CreateVpcEndpoint",
96
- "ec2:DeleteVpcEndpoint",
97
- "ec2:CreateTags",
98
- "ec2:DeleteTags",
99
- "route53:*",
100
- "sqs:*",
101
- "ssm:*",
102
- "logs:*",
103
- "sts:GetCallerIdentity",
104
- "sts:AssumeRole"
99
+ "cloudfront-keyvaluestore:*"
105
100
  ],
106
101
  "Resource": "*"
102
+ },
103
+ {
104
+ "Sid": "Lambda",
105
+ "Effect": "Allow",
106
+ "Action": "lambda:*",
107
+ "Resource": "arn:aws:lambda:{REGION}:{ACCOUNT_ID}:function:{APP_NAME}-*"
108
+ },
109
+ {
110
+ "Sid": "LambdaGlobal",
111
+ "Effect": "Allow",
112
+ "Action": [
113
+ "lambda:CreateEventSourceMapping",
114
+ "lambda:DeleteEventSourceMapping",
115
+ "lambda:GetEventSourceMapping",
116
+ "lambda:ListEventSourceMappings",
117
+ "lambda:UpdateEventSourceMapping",
118
+ "lambda:ListFunctions",
119
+ "lambda:GetFunctionConfiguration",
120
+ "lambda:TagResource",
121
+ "lambda:UntagResource",
122
+ "lambda:ListTags"
123
+ ],
124
+ "Resource": "*"
125
+ },
126
+ {
127
+ "Sid": "SQS",
128
+ "Effect": "Allow",
129
+ "Action": "sqs:*",
130
+ "Resource": "arn:aws:sqs:{REGION}:{ACCOUNT_ID}:{APP_NAME}-*"
131
+ },
132
+ {
133
+ "Sid": "IAM",
134
+ "Effect": "Allow",
135
+ "Action": "iam:*Role*",
136
+ "Resource": [
137
+ "arn:aws:iam::{ACCOUNT_ID}:role/{APP_NAME}-*",
138
+ "arn:aws:iam::{ACCOUNT_ID}:role/SchedulerRole-*"
139
+ ]
140
+ },
141
+ {
142
+ "Sid": "Scheduler",
143
+ "Effect": "Allow",
144
+ "Action": [
145
+ "scheduler:CreateScheduleGroup",
146
+ "scheduler:DeleteScheduleGroup",
147
+ "scheduler:GetScheduleGroup",
148
+ "scheduler:ListTagsForResource",
149
+ "scheduler:TagResource",
150
+ "scheduler:UntagResource"
151
+ ],
152
+ "Resource": "arn:aws:scheduler:{REGION}:{ACCOUNT_ID}:schedule-group/{APP_NAME}-*"
153
+ },
154
+ {
155
+ "Sid": "Logs",
156
+ "Effect": "Allow",
157
+ "Action": "logs:*",
158
+ "Resource": [
159
+ "arn:aws:logs:{REGION}:{ACCOUNT_ID}:log-group:/aws/lambda/{APP_NAME}-*",
160
+ "arn:aws:logs:{REGION}:{ACCOUNT_ID}:log-group:/aws/lambda/{APP_NAME}-*:*"
161
+ ]
162
+ },
163
+ {
164
+ "Sid": "LogsList",
165
+ "Effect": "Allow",
166
+ "Action": "logs:DescribeLogGroups",
167
+ "Resource": "arn:aws:logs:{REGION}:{ACCOUNT_ID}:log-group:*"
168
+ },
169
+ {
170
+ "Sid": "Discovery",
171
+ "Effect": "Allow",
172
+ "Action": "s3:ListAllMyBuckets",
173
+ "Resource": "*"
174
+ },
175
+ {
176
+ "Sid": "STS",
177
+ "Effect": "Allow",
178
+ "Action": "sts:GetCallerIdentity",
179
+ "Resource": "*"
107
180
  }
108
181
  ]
109
182
  }
110
183
  ```
111
184
 
185
+ Replace the placeholders with your values:
186
+
187
+ | Placeholder | Example | Where to find it |
188
+ |-------------|---------|-------------------|
189
+ | `{ACCOUNT_ID}` | `123456789012` | `aws sts get-caller-identity` |
190
+ | `{REGION}` | `us-east-1` | Your default AWS region |
191
+ | `{APP_NAME}` | `myapp` | The `name` in your `sst.config.ts` |
192
+
193
+ If your app creates additional S3 buckets outside the `{APP_NAME}-*` pattern, add them to the S3 statement.
194
+
112
195
  11. Click **Next**
113
196
  12. Name the policy: `everystack-dev-policy`
114
197
  13. Optionally add a description: "Permissions for everystack SST deploys"
@@ -168,6 +251,26 @@ You should see something like:
168
251
 
169
252
  If you see your account number and `everystack-dev`, your credentials are working. You are ready to deploy.
170
253
 
254
+ ### What each statement does
255
+
256
+ | Statement | Actions | Scope | Purpose |
257
+ |-----------|---------|-------|---------|
258
+ | **SSM** | `ssm:*` | `sst/*` + `{APP_NAME}/*` params | SST state management + app secrets |
259
+ | **S3** | `s3:*` | App buckets + SST state/asset buckets | Media, client bundles, OTA updates, SST state |
260
+ | **S3Protect** | Deny destructive bucket ops | `*` | Prevents `DeleteBucket`, ACL/versioning/encryption/logging changes |
261
+ | **CF** | `cloudfront:*`, `cloudfront-keyvaluestore:*` | `*` | Router distribution + KVS cache versioning |
262
+ | **Lambda** | `lambda:*` | `{APP_NAME}-*` functions | API, Image, Worker Lambdas |
263
+ | **LambdaGlobal** | Event source mappings, list, tags | `*` | SQS-to-Lambda subscriptions, resource discovery |
264
+ | **SQS** | `sqs:*` | `{APP_NAME}-*` queues | Background jobs + dead-letter queues (V3) |
265
+ | **IAM** | `iam:*Role*` | `{APP_NAME}-*` + `SchedulerRole-*` | Lambda execution roles + EventBridge Scheduler role |
266
+ | **Scheduler** | Schedule group CRUD + tags | `{APP_NAME}-*` groups | EventBridge Scheduler for delayed tasks |
267
+ | **Logs** | `logs:*` | `/aws/lambda/{APP_NAME}-*` | CloudWatch log groups for Lambda functions |
268
+ | **LogsList** | `logs:DescribeLogGroups` | `*` | Log group enumeration |
269
+ | **Discovery** | `s3:ListAllMyBuckets` | `*` | CLI resource discovery |
270
+ | **STS** | `sts:GetCallerIdentity` | `*` | Identity verification |
271
+
272
+ **V2-only apps** can omit `SQS` and `Scheduler`. **V1-only apps** can omit `SQS`, `Scheduler`, `IAM`, and `Logs`.
273
+
171
274
  ## Production hardening
172
275
 
173
- The `everystack-dev` user above has broad permissions suitable for getting started. When you are ready to harden your setup for production, read `everystack://security` for a three-profile model that separates infrastructure creation, day-to-day operations, and CI/CD into distinct IAM users with minimal permissions.
276
+ For production, read `everystack://security` for a three-profile model that separates infrastructure creation, day-to-day operations, and CI/CD into distinct IAM users with minimal permissions.
@@ -92,12 +92,12 @@ Each stage gets isolated resources (separate database, buckets, Lambda functions
92
92
  ## Deploy Flow
93
93
 
94
94
  ```bash
95
- # 1. Export Expo app
96
- pnpm export
97
-
98
- # 2. Deploy infrastructure + code
95
+ # 1. Deploy infrastructure + code
99
96
  pnpm sst deploy --stage dev
100
97
 
98
+ # 2. Export + publish app bundles
99
+ everystack update --stage dev
100
+
101
101
  # 3. Run migrations
102
102
  everystack db:migrate
103
103
 
@@ -65,9 +65,29 @@ import { imagesSchema } from '@everystack/images/schema';
65
65
  // Adds: image_variants table tracking generated variants
66
66
  ```
67
67
 
68
+ ## S3 Origin Cache
69
+
70
+ CloudFront caches are per-region. Enable `s3Cache` to persist rendered images to S3 so the first Sharp render is the last, globally:
71
+
72
+ ```typescript
73
+ export const handler = createImageHandler({
74
+ bucket: Resource.Media.name,
75
+ s3Cache: { enabled: true },
76
+ });
77
+ ```
78
+
79
+ Cache keys are deterministic from parsed params. Add a 30-day lifecycle rule on the `cache/` prefix to auto-expire stale renders. To clean up immediately when an original is deleted:
80
+
81
+ ```typescript
82
+ import { deleteImageCache } from '@everystack/server/image';
83
+ await deleteImageCache(bucket, 'uploads/photo.jpg');
84
+ ```
85
+
86
+ See `docs/caching.md` for full setup.
87
+
68
88
  ## Gotchas
69
89
 
70
90
  - Sharp runs on Lambda ARM64 (uses Lambda layer or bundled binary)
71
- - First request for a variant is slow (processing), subsequent are cached
72
- - CloudFront caches processed images (set appropriate Cache-Control)
91
+ - First request for a variant is slow (processing), subsequent are cached by CloudFront and S3
92
+ - With `s3Cache` enabled, Sharp runs once per variant globally (not once per CloudFront region)
73
93
  - AVIF encoding is slower than WebP but produces smaller files