@jaypie/mcp 0.7.2 → 0.7.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.
@@ -9,7 +9,7 @@ import { gt } from 'semver';
9
9
  /**
10
10
  * Docs Suite - Documentation services (skill, version, release_notes)
11
11
  */
12
- const BUILD_VERSION_STRING = "@jaypie/mcp@0.7.2#5d947297"
12
+ const BUILD_VERSION_STRING = "@jaypie/mcp@0.7.3#f9c2b3fe"
13
13
  ;
14
14
  const __filename$1 = fileURLToPath(import.meta.url);
15
15
  const __dirname$1 = path.dirname(__filename$1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/mcp",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,11 @@
1
+ ---
2
+ version: 1.2.26
3
+ date: 2025-01-30
4
+ summary: Auto-set PROJECT_BASE_URL on Lambda handlers
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - JaypieDistribution now automatically sets `PROJECT_BASE_URL` environment variable on Lambda handlers when host is resolved
10
+ - JaypieApiGateway now automatically sets `PROJECT_BASE_URL` environment variable on Lambda handlers when host is resolved
11
+ - This enables automatic CORS configuration when using the cors helper from `@jaypie/express`
@@ -0,0 +1,17 @@
1
+ ---
2
+ version: 1.2.6
3
+ date: 2025-01-30
4
+ summary: Improve CORS origin matching security with subdomain support
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - Fixed CORS security issue: origin matching now uses proper hostname extraction instead of `.includes()`
10
+ - Added subdomain matching: when `example.com` is allowed, subdomains like `app.example.com` are automatically allowed
11
+ - Added `extractHostname()` and `isOriginAllowed()` helpers for secure origin validation
12
+ - Origins in config can now be specified without protocol prefix (defaults to `https://`)
13
+
14
+ ## Security
15
+
16
+ - Previously, allowing `example.com` would incorrectly allow `notexample.com` or `fakeexample.com` due to `.includes()` matching
17
+ - Now uses proper hostname extraction and suffix matching with dot separator to prevent this issue
@@ -0,0 +1,14 @@
1
+ ---
2
+ version: 0.7.3
3
+ date: 2025-01-30
4
+ summary: Add CORS skill documentation comparing Jaypie cors helper with standard Express cors
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - Add `skill("cors")` documentation covering:
10
+ - Jaypie `cors` helper from `@jaypie/express`
11
+ - Comparison with standard `express/cors` middleware
12
+ - Environment variable integration (`BASE_URL`, `PROJECT_BASE_URL`)
13
+ - Sandbox mode automatic localhost handling
14
+ - Configuration options and usage patterns
package/skills/cors.md ADDED
@@ -0,0 +1,324 @@
1
+ ---
2
+ description: CORS configuration for Express.js using Jaypie's cors helper
3
+ related: express, handlers, lambda
4
+ ---
5
+
6
+ # CORS Configuration
7
+
8
+ Jaypie's `@jaypie/express` package provides a `cors` helper that wraps the standard [express/cors](https://github.com/expressjs/cors) middleware with Jaypie-specific defaults and environment awareness.
9
+
10
+ ## Quick Start
11
+
12
+ ```typescript
13
+ import express from "express";
14
+ import { cors } from "jaypie";
15
+
16
+ const app = express();
17
+
18
+ // Basic usage - environment-aware defaults
19
+ app.use(cors());
20
+
21
+ // With specific origin
22
+ app.use(cors({ origin: "https://example.com" }));
23
+
24
+ // Multiple origins
25
+ app.use(cors({ origin: ["https://a.com", "https://b.com"] }));
26
+
27
+ // Allow all origins
28
+ app.use(cors({ origin: "*" }));
29
+ ```
30
+
31
+ ## Default Behavior: `cors()`
32
+
33
+ When called with no arguments, `cors()` allows origins based on environment variables only:
34
+
35
+ | Condition | Allowed |
36
+ |-----------|---------|
37
+ | No `Origin` header (curl, mobile, server-to-server) | ✅ Always |
38
+ | Origin matches `BASE_URL` env var | ✅ Yes |
39
+ | Origin matches `PROJECT_BASE_URL` env var | ✅ Yes |
40
+ | Origin is subdomain of allowed origin | ✅ Yes |
41
+ | `PROJECT_ENV=sandbox` and origin is `localhost[:port]` | ✅ Yes |
42
+ | Any other cross-origin request | ❌ Rejected |
43
+
44
+ ### Subdomain Matching
45
+
46
+ When you allow a domain, all subdomains of that domain are also automatically allowed:
47
+
48
+ ```typescript
49
+ // Allow example.com and all its subdomains
50
+ app.use(cors({ origin: "example.com" }));
51
+ // Allows: https://example.com, https://app.example.com, https://sub.app.example.com
52
+ // Rejects: https://notexample.com, https://fakeexample.com
53
+
54
+ // Protocol prefix is optional in config
55
+ app.use(cors({ origin: "example.com" })); // Same as https://example.com
56
+ ```
57
+
58
+ This is secure because it uses proper hostname extraction and suffix matching with a dot separator—domains like `notexample.com` or `fakeexample.com` are correctly rejected.
59
+
60
+ **Important:** With no environment variables set and not in sandbox mode, `cors()` rejects all cross-origin browser requests. This is secure by default—you must explicitly configure allowed origins.
61
+
62
+ ```typescript
63
+ // Example: Production environment
64
+ // BASE_URL=https://api.example.com
65
+ // PROJECT_BASE_URL=https://example.com
66
+
67
+ app.use(cors());
68
+ // Allows: https://api.example.com, https://example.com
69
+ // Rejects: all other origins
70
+
71
+ // Example: Sandbox/development
72
+ // PROJECT_ENV=sandbox
73
+
74
+ app.use(cors());
75
+ // Allows: http://localhost, http://localhost:3000, http://localhost:5173, etc.
76
+ // Rejects: all other origins (unless BASE_URL/PROJECT_BASE_URL set)
77
+ ```
78
+
79
+ ## Jaypie vs Standard Express CORS
80
+
81
+ ### Key Differences
82
+
83
+ | Feature | Standard `cors` | Jaypie `cors` |
84
+ |---------|-----------------|---------------|
85
+ | **Origin Validation** | Static or manual callback | Dynamic environment-aware callback |
86
+ | **Subdomain Matching** | Manual implementation | Automatic (subdomains of allowed origins permitted) |
87
+ | **Environment URLs** | Not supported | Reads `BASE_URL`, `PROJECT_BASE_URL` |
88
+ | **Sandbox Mode** | Manual localhost config | Auto-allows localhost in sandbox |
89
+ | **Error Response** | Generic CORS error | Returns `CorsError` with JSON body |
90
+ | **No-Origin Requests** | Configurable | Always allowed (mobile apps, curl) |
91
+ | **Protocol Prefix** | Required in config | Optional (defaults to `https://`) |
92
+
93
+ ### Jaypie-Specific Behavior
94
+
95
+ 1. **Environment Variable Integration**
96
+ - `BASE_URL` - Automatically added to allowed origins
97
+ - `PROJECT_BASE_URL` - Automatically added to allowed origins
98
+ - Protocol auto-added if missing (defaults to `https://`)
99
+
100
+ 2. **Sandbox Mode** (when `PROJECT_ENV=sandbox` or `PROJECT_SANDBOX_MODE=true`)
101
+ - `http://localhost` is automatically allowed
102
+ - `http://localhost:*` (any port) is automatically allowed
103
+
104
+ 3. **No-Origin Requests**
105
+ - Requests without an `Origin` header are always allowed
106
+ - Enables mobile apps, server-to-server calls, and curl testing
107
+
108
+ 4. **Error Handling**
109
+ - Invalid origins return a `CorsError` from `@jaypie/errors`
110
+ - Response includes JSON body with error details
111
+
112
+ ## Configuration Options
113
+
114
+ ### Jaypie `cors` Options
115
+
116
+ ```typescript
117
+ interface CorsConfig {
118
+ origin?: string | string[]; // Additional allowed origins
119
+ overrides?: Record<string, unknown>; // Pass-through to express/cors
120
+ }
121
+ ```
122
+
123
+ ### Standard `cors` Options (via `overrides`)
124
+
125
+ All standard `cors` options can be passed through the `overrides` parameter:
126
+
127
+ ```typescript
128
+ import { cors } from "jaypie";
129
+
130
+ app.use(cors({
131
+ origin: "https://example.com",
132
+ overrides: {
133
+ methods: ["GET", "POST", "PUT"],
134
+ allowedHeaders: ["Content-Type", "Authorization"],
135
+ exposedHeaders: ["X-Request-Id"],
136
+ credentials: true,
137
+ maxAge: 86400,
138
+ optionsSuccessStatus: 200,
139
+ preflightContinue: false,
140
+ },
141
+ }));
142
+ ```
143
+
144
+ ### Full Standard Options Reference
145
+
146
+ | Option | Type | Default | Description |
147
+ |--------|------|---------|-------------|
148
+ | `methods` | `string \| string[]` | All methods | Allowed HTTP methods |
149
+ | `allowedHeaders` | `string \| string[]` | Reflect request | Allowed request headers |
150
+ | `exposedHeaders` | `string \| string[]` | None | Headers exposed to browser |
151
+ | `credentials` | `boolean` | `false` | Allow credentials (cookies) |
152
+ | `maxAge` | `number` | None | Preflight cache duration (seconds) |
153
+ | `preflightContinue` | `boolean` | `false` | Pass OPTIONS to next handler |
154
+ | `optionsSuccessStatus` | `number` | `204` | Status for successful OPTIONS |
155
+
156
+ ## Usage Patterns
157
+
158
+ ### API with Multiple Allowed Origins
159
+
160
+ ```typescript
161
+ import { cors } from "jaypie";
162
+
163
+ // Production + staging + local origins
164
+ app.use(cors({
165
+ origin: [
166
+ "https://app.example.com",
167
+ "https://staging.example.com",
168
+ ],
169
+ }));
170
+ ```
171
+
172
+ ### API with Credentials
173
+
174
+ ```typescript
175
+ import { cors } from "jaypie";
176
+
177
+ // Enable cookies for cross-origin requests
178
+ app.use(cors({
179
+ origin: "https://app.example.com",
180
+ overrides: {
181
+ credentials: true,
182
+ },
183
+ }));
184
+ ```
185
+
186
+ ### Public API (Allow All Origins)
187
+
188
+ ```typescript
189
+ import { cors } from "jaypie";
190
+
191
+ // Allow any origin
192
+ app.use(cors({ origin: "*" }));
193
+ ```
194
+
195
+ ### Per-Route CORS
196
+
197
+ ```typescript
198
+ import express from "express";
199
+ import { cors } from "jaypie";
200
+
201
+ const app = express();
202
+
203
+ // Public endpoints - wide open
204
+ app.use("/api/public", cors({ origin: "*" }));
205
+
206
+ // Protected endpoints - restricted origins
207
+ app.use("/api/admin", cors({
208
+ origin: "https://admin.example.com",
209
+ overrides: { credentials: true },
210
+ }));
211
+ ```
212
+
213
+ ## How Origin Validation Works
214
+
215
+ Jaypie's `cors` uses a dynamic origin callback that checks origins in this order:
216
+
217
+ 1. **Wildcard** - If `origin: "*"`, allow all origins
218
+ 2. **No Origin** - Allow requests without Origin header (mobile, curl, etc.)
219
+ 3. **Environment URLs** - Check `BASE_URL` and `PROJECT_BASE_URL`
220
+ 4. **Configured Origins** - Check origins passed to `cors({ origin: [...] })`
221
+ 5. **Sandbox Localhost** - In sandbox mode, allow localhost with any port
222
+
223
+ For each allowed origin, the validation checks:
224
+ - **Exact hostname match** - `example.com` matches `https://example.com`
225
+ - **Subdomain match** - `example.com` matches `https://app.example.com`
226
+
227
+ ```typescript
228
+ // Simplified origin validation logic
229
+ const isAllowed =
230
+ origin === "*" ||
231
+ !requestOrigin ||
232
+ isOriginAllowed(requestOrigin, process.env.BASE_URL) ||
233
+ isOriginAllowed(requestOrigin, process.env.PROJECT_BASE_URL) ||
234
+ configuredOrigins.some(o => isOriginAllowed(requestOrigin, o)) ||
235
+ (isSandbox && requestOrigin.match(/^http:\/\/localhost(:\d+)?$/));
236
+
237
+ // Where isOriginAllowed checks for exact match OR subdomain match
238
+ // e.g., "app.example.com".endsWith(".example.com") → true
239
+ ```
240
+
241
+ ## Error Responses
242
+
243
+ When an origin is not allowed, Jaypie returns a structured error response:
244
+
245
+ ```json
246
+ {
247
+ "errors": [{
248
+ "title": "CorsError",
249
+ "status": 403,
250
+ "detail": "Cross-Origin Request Blocked"
251
+ }]
252
+ }
253
+ ```
254
+
255
+ ## Comparison: Standard vs Jaypie CORS Setup
256
+
257
+ ### Standard Express CORS
258
+
259
+ ```typescript
260
+ import express from "express";
261
+ import cors from "cors";
262
+
263
+ const app = express();
264
+
265
+ // Manual origin validation
266
+ app.use(cors({
267
+ origin: (origin, callback) => {
268
+ const allowedOrigins = [
269
+ process.env.BASE_URL,
270
+ process.env.PROJECT_BASE_URL,
271
+ "https://app.example.com",
272
+ ];
273
+
274
+ // Allow no-origin requests
275
+ if (!origin) {
276
+ return callback(null, true);
277
+ }
278
+
279
+ // Check allowed list
280
+ if (allowedOrigins.some(o => origin.includes(o))) {
281
+ return callback(null, true);
282
+ }
283
+
284
+ // Sandbox localhost
285
+ if (process.env.PROJECT_ENV === "sandbox" &&
286
+ origin.match(/^http:\/\/localhost(:\d+)?$/)) {
287
+ return callback(null, true);
288
+ }
289
+
290
+ callback(new Error("Not allowed by CORS"));
291
+ },
292
+ credentials: true,
293
+ }));
294
+ ```
295
+
296
+ ### Jaypie CORS (Equivalent)
297
+
298
+ ```typescript
299
+ import express from "express";
300
+ import { cors } from "jaypie";
301
+
302
+ const app = express();
303
+
304
+ // Same behavior, less code
305
+ app.use(cors({
306
+ origin: "https://app.example.com",
307
+ overrides: { credentials: true },
308
+ }));
309
+ ```
310
+
311
+ ## Environment Variables
312
+
313
+ | Variable | Description |
314
+ |----------|-------------|
315
+ | `BASE_URL` | Automatically added to allowed origins |
316
+ | `PROJECT_BASE_URL` | Automatically added to allowed origins |
317
+ | `PROJECT_ENV` | Set to `sandbox` to enable localhost access |
318
+ | `PROJECT_SANDBOX_MODE` | Set to `true` to enable localhost access |
319
+
320
+ ## See Also
321
+
322
+ - **`skill("express")`** - Express handler and Lambda adapter documentation
323
+ - **`skill("handlers")`** - Jaypie handler lifecycle documentation
324
+ - **`skill("lambda")`** - AWS Lambda deployment patterns
package/skills/express.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Express.js handler for AWS Lambda with API Gateway support
3
- related: aws, cdk, handlers, lambda, services, streaming
3
+ related: aws, cdk, cors, handlers, lambda, services, streaming
4
4
  ---
5
5
 
6
6
  # Express Integration