@jaypie/mcp 0.7.1 → 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.1#82d19d8a"
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.1",
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,18 @@
1
+ ---
2
+ version: 0.7.2
3
+ date: 2026-01-30
4
+ summary: Document JaypieNextJs streaming configuration requirements
5
+ ---
6
+
7
+ ## Documentation
8
+
9
+ - Added JaypieNextJs streaming documentation to `streaming.md` skill
10
+ - Added JaypieNextJs construct to `cdk.md` skill
11
+ - Clarified that `streaming: true` requires `open-next.config.ts` with `aws-lambda-streaming` wrapper
12
+
13
+ ## Context
14
+
15
+ Issue #171 reported that `JaypieNextJs` with `streaming: true` returns a JSON envelope instead of streamed HTML. This is expected behavior - Lambda response streaming requires both:
16
+
17
+ 1. CDK: `streaming: true` (configures `InvokeMode: RESPONSE_STREAM`)
18
+ 2. Next.js: `open-next.config.ts` with `wrapper: "aws-lambda-streaming"`
@@ -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/cdk.md CHANGED
@@ -139,7 +139,24 @@ const handler = new JaypieLambda(this, "Handler", {
139
139
  });
140
140
  ```
141
141
 
142
+ ## JaypieNextJs
143
+
144
+ Deploy Next.js applications:
145
+
146
+ ```typescript
147
+ import { JaypieNextJs } from "@jaypie/constructs";
148
+
149
+ new JaypieNextJs(this, "App", {
150
+ nextjsPath: "../nextjs",
151
+ domainName: "app.example.com",
152
+ hostedZone: "example.com",
153
+ streaming: true, // Optional: enables response streaming
154
+ });
155
+ ```
156
+
157
+ **Streaming Note:** When `streaming: true`, also create `open-next.config.ts` in your Next.js app with `wrapper: "aws-lambda-streaming"`. See `skill("streaming")` for details.
158
+
142
159
  ## See Also
143
160
 
144
- - **`skill("streaming")`** - JaypieDistribution with `streaming: true` for response streaming
161
+ - **`skill("streaming")`** - JaypieDistribution and JaypieNextJs streaming configuration
145
162
  - **`skill("websockets")`** - JaypieWebSocket and JaypieWebSocketTable constructs
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
@@ -76,6 +76,8 @@ export const handler = lambdaStreamHandler(async (event, context) => {
76
76
 
77
77
  ## CDK Configuration
78
78
 
79
+ ### JaypieDistribution
80
+
79
81
  ```typescript
80
82
  import { JaypieLambda, JaypieDistribution } from "@jaypie/constructs";
81
83
 
@@ -90,6 +92,40 @@ new JaypieDistribution(this, "Api", {
90
92
  });
91
93
  ```
92
94
 
95
+ ### JaypieNextJs Streaming
96
+
97
+ For Next.js applications with `JaypieNextJs`, streaming requires **two** configurations:
98
+
99
+ 1. **CDK** - Set `streaming: true` in the construct
100
+ 2. **Next.js App** - Create `open-next.config.ts` with the streaming wrapper
101
+
102
+ ```typescript
103
+ // CDK Stack
104
+ import { JaypieNextJs } from "@jaypie/constructs";
105
+
106
+ new JaypieNextJs(this, "App", {
107
+ nextjsPath: "../nextjs",
108
+ streaming: true,
109
+ });
110
+ ```
111
+
112
+ ```typescript
113
+ // nextjs/open-next.config.ts (required for streaming)
114
+ import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js";
115
+
116
+ const config = {
117
+ default: {
118
+ override: {
119
+ wrapper: "aws-lambda-streaming",
120
+ },
121
+ },
122
+ } satisfies OpenNextConfig;
123
+
124
+ export default config;
125
+ ```
126
+
127
+ **Important:** Without `open-next.config.ts`, the Lambda returns a JSON envelope `{ statusCode, headers, body }` instead of streaming HTML. This is because `cdk-nextjs-standalone` configures the Lambda Function URL with `RESPONSE_STREAM` invoke mode, but OpenNext also needs to be configured to use the streaming wrapper.
128
+
93
129
  ## Error Handling
94
130
 
95
131
  Errors are written to the stream in the configured format: