@jaypie/mcp 0.2.6 → 0.2.8

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/index.js CHANGED
@@ -885,7 +885,7 @@ function listLlmProviders() {
885
885
  };
886
886
  }
887
887
 
888
- const BUILD_VERSION_STRING = "@jaypie/mcp@0.2.6#e43f0153"
888
+ const BUILD_VERSION_STRING = "@jaypie/mcp@0.2.8"
889
889
  ;
890
890
  const __filename$1 = fileURLToPath(import.meta.url);
891
891
  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.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -153,12 +153,44 @@ Note: API Gateway has a 29-second timeout limit. For longer streaming operations
153
153
 
154
154
  ### Stack Types
155
155
 
156
- Use specialized stacks for different purposes:
156
+ Always extend Jaypie stack classes instead of raw CDK classes:
157
+
158
+ | Use | Instead of |
159
+ |-----|------------|
160
+ | `JaypieAppStack` | `cdk.Stack` |
161
+ | `JaypieInfrastructureStack` | `cdk.Stack` |
162
+
163
+ Jaypie stacks automatically configure:
164
+ - `env` with `CDK_DEFAULT_ACCOUNT` and `CDK_DEFAULT_REGION` (required for context providers)
165
+ - Standard tagging
166
+ - Removal policies based on environment
167
+
157
168
  ```typescript
158
169
  new JaypieAppStack(scope, "AppStack"); // Application resources
159
170
  new JaypieInfrastructureStack(scope, "InfraStack"); // Infrastructure resources
160
171
  ```
161
172
 
173
+ #### Error: Using cdk.Stack with Context Providers
174
+
175
+ Using `cdk.Stack` directly with constructs that need context providers causes:
176
+
177
+ ```
178
+ ValidationError: Cannot retrieve value from context provider hosted-zone since
179
+ account/region are not specified at the stack level.
180
+ ```
181
+
182
+ **Solution:** Change the base class:
183
+
184
+ ```typescript
185
+ // Wrong
186
+ import * as cdk from "aws-cdk-lib";
187
+ export class AppStack extends cdk.Stack { ... }
188
+
189
+ // Correct
190
+ import { JaypieAppStack } from "@jaypie/constructs";
191
+ export class AppStack extends JaypieAppStack { ... }
192
+ ```
193
+
162
194
  ### Secrets Management
163
195
 
164
196
  Use `JaypieEnvSecret` for cross-stack secret sharing:
@@ -314,7 +346,7 @@ const table = new JaypieDynamoDb(this, "MyTable", {
314
346
  // Connect table to Lambda
315
347
  new JaypieLambda(this, "Worker", {
316
348
  code: "dist",
317
- tables: [table], // Grants read/write, sets CDK_ENV_DYNAMO_TABLE
349
+ tables: [table], // Grants read/write, sets DYNAMODB_TABLE_NAME
318
350
  });
319
351
  ```
320
352
 
@@ -190,6 +190,25 @@ Pass secrets only to steps that need them:
190
190
 
191
191
  Never expose secrets in logs or workflow outputs.
192
192
 
193
+ ## CDK Stack Names
194
+
195
+ The `stack-name` parameter in deploy workflows must match the stack ID defined in `packages/cdk/bin/cdk.ts`:
196
+
197
+ ```typescript
198
+ // bin/cdk.ts
199
+ new AppStack(app, "AppStack"); // "AppStack" is the stack ID
200
+ ```
201
+
202
+ ```yaml
203
+ # deploy-*.yml
204
+ - name: Deploy CDK Stack
205
+ uses: ./.github/actions/cdk-deploy
206
+ with:
207
+ stack-name: AppStack # Must match the stack ID above
208
+ ```
209
+
210
+ If you rename the stack ID (e.g., to "MyProjectAppStack" for clarity in AWS), update all deploy workflows to match. Mismatched names cause "No stacks match the name(s)" errors.
211
+
193
212
  ## Integrations
194
213
 
195
214
  ### AWS
@@ -359,3 +359,259 @@ class TenantFabricator {
359
359
  4. **Seed Flexibility**: Accepts strings, numbers, or UUIDs as seeds
360
360
  5. **Hierarchical Generation**: Nested fabricators enable deterministic tree structures for complex data models
361
361
  6. **Identity & Naming**: Every fabricator has a unique ID and name for tracking and debugging
362
+
363
+ ## EventFabricator: Temporal Event Generation
364
+
365
+ ### Overview
366
+
367
+ `EventFabricator` is an abstract base class for generating temporally-distributed events across a year. It builds on `Fabricator` to add:
368
+ - Annual event counts with configurable distribution
369
+ - Temporal templates for hour, day, week, month, and date weighting
370
+ - Timezone-aware hour shifting
371
+ - Derived event system for cascading events
372
+
373
+ ### Basic Usage
374
+
375
+ ```typescript
376
+ import {
377
+ EventFabricator,
378
+ HOURS_BUSINESS,
379
+ DAYS_WEEKDAYS_ONLY,
380
+ type CreateEventParams,
381
+ } from "@jaypie/fabricator";
382
+
383
+ interface SimpleEvent {
384
+ id: string;
385
+ timestamp: Date;
386
+ }
387
+
388
+ class SimpleEventFabricator extends EventFabricator<SimpleEvent> {
389
+ protected createEvent({ seed, timestamp }: CreateEventParams): SimpleEvent {
390
+ return { id: seed, timestamp };
391
+ }
392
+ }
393
+
394
+ const fab = new SimpleEventFabricator({
395
+ seed: "my-events",
396
+ annualCount: 1000,
397
+ template: [HOURS_BUSINESS, DAYS_WEEKDAYS_ONLY],
398
+ });
399
+
400
+ const events = fab.events({ year: 2025 });
401
+ // Returns 1000 events during business hours on weekdays
402
+ ```
403
+
404
+ ### Temporal Templates
405
+
406
+ Templates control event distribution. They can be combined:
407
+
408
+ **Hour Templates:**
409
+ - `HOURS_BUSINESS` - 8am-5pm
410
+ - `HOURS_RETAIL` - 10am-8pm
411
+ - `HOURS_EVENING` - 6pm-10pm
412
+ - `HOURS_24_7` - All hours equal
413
+
414
+ **Day Templates:**
415
+ - `DAYS_WEEKDAYS_ONLY` - Mon-Fri only
416
+ - `DAYS_NO_SUNDAY` - All but Sunday
417
+ - `DAYS_NO_MONDAY` - All but Monday
418
+
419
+ **Curve Templates (gradual peaks):**
420
+ - `CURVE_EVENING_PEAK` - Peaks at 7pm
421
+ - `CURVE_ECOMMERCE` - Peaks at 8pm
422
+ - `CURVE_MIDDAY_PEAK` - Peaks at 11am
423
+
424
+ **Spike Templates (sharp peaks):**
425
+ - `SPIKE_MORNING` - 7-8am peak
426
+ - `SPIKE_LUNCH` - 12-1pm peak
427
+ - `SPIKE_EVENING` - 6-7pm peak
428
+
429
+ **Boost/Lull Templates (multipliers):**
430
+ - `BOOST_SUMMER`, `LULL_SUMMER`
431
+ - `BOOST_WINTER`, `LULL_WINTER`
432
+ - `BOOST_WEEKENDS`, `LULL_WEEKENDS`
433
+ - `BOOST_HOLIDAY_SEASON` - Nov-Dec 1.5x
434
+
435
+ ```typescript
436
+ const fab = new MyFabricator({
437
+ template: [
438
+ HOURS_BUSINESS,
439
+ DAYS_WEEKDAYS_ONLY,
440
+ BOOST_HOLIDAY_SEASON,
441
+ CURVE_MIDDAY_PEAK,
442
+ ],
443
+ });
444
+ ```
445
+
446
+ ## Derived Events: Cascading Event Generation
447
+
448
+ ### Overview
449
+
450
+ The derived event system allows events to spawn follow-up events based on probabilistic rules. This is ideal for modeling:
451
+ - Financial transactions with voids, refunds, chargebacks
452
+ - Subscription renewals with payment retries
453
+ - Any event chains with cause-and-effect relationships
454
+
455
+ ### Configuration
456
+
457
+ ```typescript
458
+ import {
459
+ EventFabricator,
460
+ CHANCE,
461
+ type DerivedConfig,
462
+ type TimestampedEvent,
463
+ } from "@jaypie/fabricator";
464
+
465
+ interface Transaction extends TimestampedEvent {
466
+ id: string;
467
+ amount: number;
468
+ type: "purchase" | "void" | "refund" | "chargeback";
469
+ parentId?: string;
470
+ timestamp: Date;
471
+ }
472
+
473
+ const derivedConfig: DerivedConfig<Transaction> = {
474
+ rules: [
475
+ {
476
+ name: "void",
477
+ probability: CHANCE.RARE, // 2.1%
478
+ condition: (parent) => parent.type === "purchase",
479
+ timing: { mode: "same-day" },
480
+ createDerived: ({ parent, seed, timestamp }) => ({
481
+ id: seed,
482
+ type: "void",
483
+ amount: -parent.amount,
484
+ timestamp,
485
+ parentId: parent.id,
486
+ }),
487
+ },
488
+ {
489
+ name: "refund",
490
+ probability: 0.10, // 10%
491
+ timing: {
492
+ mode: "range",
493
+ delayMin: 1,
494
+ delayMax: 14,
495
+ unit: "days",
496
+ },
497
+ createDerived: ({ parent, seed, timestamp }) => ({
498
+ id: seed,
499
+ type: "refund",
500
+ amount: -parent.amount,
501
+ timestamp,
502
+ parentId: parent.id,
503
+ }),
504
+ },
505
+ ],
506
+ boundaryBehavior: "include", // include | exclude | clamp
507
+ maxDepth: 4, // Prevent infinite chains
508
+ };
509
+ ```
510
+
511
+ ### Timing Modes
512
+
513
+ ```typescript
514
+ interface DerivedTiming {
515
+ mode: "same-day" | "fixed" | "range" | "recurring";
516
+ delayMin?: number;
517
+ delayMax?: number;
518
+ unit?: "days" | "weeks" | "months" | "hours" | "minutes" | "seconds";
519
+ interval?: number; // For recurring
520
+ maxRecurrences?: number; // For recurring
521
+ until?: Date | "end-of-year";
522
+ }
523
+ ```
524
+
525
+ **Examples:**
526
+ - Same day: `{ mode: "same-day" }`
527
+ - Fixed delay: `{ mode: "fixed", delayMin: 7, unit: "days" }`
528
+ - Range: `{ mode: "range", delayMin: 1, delayMax: 14, unit: "days" }`
529
+ - Monthly recurring: `{ mode: "recurring", interval: 1, unit: "months", until: "end-of-year" }`
530
+
531
+ ### Nested Derived Events (Chains)
532
+
533
+ Derived events can spawn their own derived events:
534
+
535
+ ```typescript
536
+ {
537
+ name: "chargeback",
538
+ probability: CHANCE.RARE,
539
+ timing: { mode: "range", delayMin: 30, delayMax: 90, unit: "days" },
540
+ createDerived: ({ parent, seed, timestamp }) => ({
541
+ id: seed,
542
+ type: "chargeback",
543
+ amount: -parent.amount,
544
+ timestamp,
545
+ parentId: parent.id,
546
+ }),
547
+ derived: [ // Nested rules for chargebacks
548
+ {
549
+ name: "representment",
550
+ probability: 0.70, // 70% of chargebacks
551
+ timing: { mode: "range", delayMin: 7, delayMax: 21, unit: "days" },
552
+ createDerived: ({ parent, seed, timestamp }) => ({
553
+ id: seed,
554
+ type: "representment",
555
+ amount: Math.abs(parent.amount), // Re-charge
556
+ timestamp,
557
+ parentId: parent.id,
558
+ }),
559
+ },
560
+ ],
561
+ }
562
+ ```
563
+
564
+ ### Using with EventFabricator
565
+
566
+ ```typescript
567
+ class TransactionFabricator extends EventFabricator<Transaction> {
568
+ constructor(options = {}) {
569
+ super({
570
+ ...options,
571
+ derived: derivedConfig,
572
+ template: [HOURS_BUSINESS, DAYS_WEEKDAYS_ONLY],
573
+ });
574
+ }
575
+
576
+ protected createEvent({ seed, timestamp }: CreateEventParams): Transaction {
577
+ const fab = new Fabricator({ seed });
578
+ return {
579
+ id: seed,
580
+ type: "purchase",
581
+ amount: fab.random({ min: 10, max: 500, currency: true }),
582
+ timestamp,
583
+ };
584
+ }
585
+ }
586
+
587
+ const fab = new TransactionFabricator({
588
+ seed: "my-business",
589
+ annualCount: 1000,
590
+ });
591
+
592
+ // Get all events including derived events
593
+ const events = fab.events({ year: 2025 });
594
+
595
+ // Get events with metadata (parent refs, depth, rule names)
596
+ const eventsWithMeta = fab.eventsWithMeta({ year: 2025 });
597
+ ```
598
+
599
+ ### Event Metadata
600
+
601
+ `eventsWithMeta()` returns events with relationship data:
602
+
603
+ ```typescript
604
+ interface EventWithDerivedMeta<T> {
605
+ event: T;
606
+ depth: number; // 0 = primary, 1 = first derived, etc.
607
+ parentSeed?: string; // Seed of parent event
608
+ ruleName?: string; // Rule that created this derived event
609
+ }
610
+ ```
611
+
612
+ ### Key Properties
613
+
614
+ 1. **Deterministic**: Same seed produces same derived event cascade
615
+ 2. **Chronological**: All events (primary + derived) sorted by timestamp
616
+ 3. **Depth-Limited**: `maxDepth` prevents infinite chains
617
+ 4. **Boundary Control**: Events outside target year can be included, excluded, or clamped
@@ -1115,6 +1115,22 @@ The environment must be created in GitHub repository settings before the workflo
1115
1115
  - Verify the AWS role has permissions to access any Secrets Manager secrets
1116
1116
  - Review CloudFormation events in the AWS Console for specific errors
1117
1117
 
1118
+ ### Error: "Cannot retrieve value from context provider hosted-zone"
1119
+
1120
+ Stacks using context providers must extend `JaypieAppStack`, not `cdk.Stack`:
1121
+
1122
+ ```typescript
1123
+ // Wrong
1124
+ import * as cdk from "aws-cdk-lib";
1125
+ export class AppStack extends cdk.Stack { ... }
1126
+
1127
+ // Correct
1128
+ import { JaypieAppStack } from "@jaypie/constructs";
1129
+ export class AppStack extends JaypieAppStack { ... }
1130
+ ```
1131
+
1132
+ `JaypieAppStack` automatically sets `env` with `CDK_DEFAULT_ACCOUNT` and `CDK_DEFAULT_REGION`.
1133
+
1118
1134
  ### Variables Not Being Applied
1119
1135
 
1120
1136
  - Composite actions cannot access `vars.*` directly
@@ -162,6 +162,7 @@ Context callbacks connect to adapter registration:
162
162
  | `String` | `"string"`, `""` | String coercion |
163
163
  | `Number` | `"number"` | Number coercion |
164
164
  | `Boolean` | `"boolean"` | Boolean coercion |
165
+ | `Date` | `DateType` | Date coercion (ISO strings, timestamps) |
165
166
  | `Array` | `"array"`, `[]` | Array coercion |
166
167
  | `Object` | `"object"`, `{}` | Object coercion |
167
168
  | `[String]` | `[""]` | Typed array of strings |
@@ -171,6 +172,7 @@ Context callbacks connect to adapter registration:
171
172
  | `/regex/` | - | String with regex validation |
172
173
  | `["a", "b"]` | - | Validated string (must match) |
173
174
  | `[1, 2, 3]` | - | Validated number (must match) |
175
+ | `StatusType` | - | Validated status ("pending", "processing", etc.) |
174
176
 
175
177
  ### Coercion Examples
176
178
 
@@ -201,6 +203,14 @@ coerce([1, 2], [String]); // → ["1", "2"]
201
203
  coerce({ value: "42" }, Number); // → 42
202
204
  coerce(["true"], Boolean); // → true
203
205
  coerce('{"value": 5}', Number); // → 5
206
+
207
+ // Date coercion
208
+ import { coerceToDate, coerceFromDate } from "@jaypie/vocabulary";
209
+
210
+ coerceToDate("2026-01-15T10:30:00Z"); // → Date object
211
+ coerceToDate(1736942400000); // → Date from timestamp
212
+ coerceFromDate(new Date(), String); // → ISO string
213
+ coerceFromDate(new Date(), Number); // → Unix timestamp (ms)
204
214
  ```
205
215
 
206
216
  ### RegExp Type Shorthand
@@ -236,6 +246,30 @@ input: {
236
246
  }
237
247
  ```
238
248
 
249
+ ### StatusType
250
+
251
+ A predefined validated string type for common status values:
252
+
253
+ ```typescript
254
+ import { StatusType, isStatus, STATUS_VALUES } from "@jaypie/vocabulary";
255
+
256
+ // StatusType is: ["canceled", "complete", "error", "pending", "processing", "queued", "sending"]
257
+
258
+ const handler = serviceHandler({
259
+ input: {
260
+ status: { type: StatusType, default: "pending" },
261
+ },
262
+ service: ({ status }) => status,
263
+ });
264
+
265
+ await handler({ status: "processing" }); // ✓
266
+ await handler({ status: "invalid" }); // ✗ BadRequestError
267
+
268
+ // Type guard
269
+ isStatus("pending"); // → true
270
+ isStatus("unknown"); // → false
271
+ ```
272
+
239
273
  ## Entity Types
240
274
 
241
275
  The vocabulary provides standard entity types for consistent data modeling:
@@ -302,6 +336,40 @@ progress?:
302
336
  nextPercentageCheckpoint?: Number
303
337
  ```
304
338
 
339
+ ### BaseEntity Utilities
340
+
341
+ Field constants and utility functions for working with entities:
342
+
343
+ ```typescript
344
+ import {
345
+ // Field name constants
346
+ BASE_ENTITY_FIELDS, // All field names as constants
347
+ BASE_ENTITY_REQUIRED_FIELDS, // ["createdAt", "id", "model", "updatedAt"]
348
+ BASE_ENTITY_AUTO_FIELDS, // ["createdAt", "history", "id", "updatedAt"]
349
+ BASE_ENTITY_TIMESTAMP_FIELDS, // ["archivedAt", "createdAt", "deletedAt", "updatedAt"]
350
+
351
+ // Type guards
352
+ isBaseEntity, // Check if value is a complete BaseEntity
353
+ hasBaseEntityShape, // Check if value has minimum shape (id + model)
354
+
355
+ // Field helpers
356
+ isAutoField, // Check if field is auto-generated
357
+ isTimestampField, // Check if field is a timestamp
358
+
359
+ // Utilities
360
+ createBaseEntityInput, // Create minimal input with required model
361
+ pickBaseEntityFields, // Extract only BaseEntity fields from object
362
+ } from "@jaypie/vocabulary";
363
+
364
+ // Example: Check if a field should be auto-generated
365
+ isAutoField("id"); // → true
366
+ isAutoField("name"); // → false
367
+
368
+ // Example: Extract BaseEntity fields from mixed object
369
+ const mixed = { id: "123", model: "record", customField: "value" };
370
+ pickBaseEntityFields(mixed); // → { id: "123", model: "record" }
371
+ ```
372
+
305
373
  ## TypeScript Types
306
374
 
307
375
  ```typescript
@@ -315,20 +383,22 @@ import type {
315
383
  Job,
316
384
  MessageEntity,
317
385
  Progress,
386
+ Status,
318
387
 
319
388
  // Message types
320
389
  Message,
321
390
  MessageLevel,
322
391
 
323
392
  // Coercion types
393
+ ArrayElementType,
324
394
  CoercionType,
325
- ScalarType,
326
395
  CompositeType,
396
+ DateCoercionType,
397
+ RegExpType,
398
+ ScalarType,
327
399
  TypedArrayType,
328
- ValidatedStringType,
329
400
  ValidatedNumberType,
330
- RegExpType,
331
- ArrayElementType,
401
+ ValidatedStringType,
332
402
 
333
403
  // Service handler types
334
404
  InputFieldDefinition,
@@ -358,6 +428,20 @@ interface Message {
358
428
  ### Main Export (`@jaypie/vocabulary`)
359
429
 
360
430
  ```typescript
431
+ // BaseEntity utilities
432
+ export {
433
+ BASE_ENTITY_AUTO_FIELDS,
434
+ BASE_ENTITY_FIELDS,
435
+ BASE_ENTITY_REQUIRED_FIELDS,
436
+ BASE_ENTITY_TIMESTAMP_FIELDS,
437
+ createBaseEntityInput,
438
+ hasBaseEntityShape,
439
+ isAutoField,
440
+ isBaseEntity,
441
+ isTimestampField,
442
+ pickBaseEntityFields,
443
+ } from "./base-entity.js";
444
+
361
445
  // Coercion functions
362
446
  export {
363
447
  coerce,
@@ -370,17 +454,28 @@ export {
370
454
  coerceToString,
371
455
  } from "./coerce.js";
372
456
 
457
+ // Date coercion
458
+ export {
459
+ coerceFromDate,
460
+ coerceToDate,
461
+ DateType,
462
+ isDateType,
463
+ isValidDate,
464
+ } from "./coerce-date.js";
465
+
466
+ // Status type
467
+ export { isStatus, STATUS_VALUES, StatusType } from "./status.js";
468
+
373
469
  // Service Handler
374
470
  export { serviceHandler } from "./serviceHandler.js";
375
471
 
376
- // Adapter namespaces
377
- export * as commander from "./commander/index.js";
378
- export * as lambda from "./lambda/index.js";
472
+ // LLM adapter (re-exported, no optional deps)
379
473
  export * as llm from "./llm/index.js";
380
- export * as mcp from "./mcp/index.js";
381
474
 
382
- // Types (including Message vocabulary and ServiceContext)
383
- export type { Message, MessageLevel, ServiceContext } from "./types.js";
475
+ // Note: Other adapters have optional dependencies and must be imported directly:
476
+ // import { registerServiceCommand } from "@jaypie/vocabulary/commander";
477
+ // import { lambdaServiceHandler } from "@jaypie/vocabulary/lambda";
478
+ // import { registerMcpTool } from "@jaypie/vocabulary/mcp";
384
479
 
385
480
  // Version
386
481
  export const VOCABULARY_VERSION: string;
@@ -22,6 +22,8 @@ new InfrastructureStack(app, "InfrastructureStack");
22
22
  new AppStack(app, "AppStack");
23
23
  ```
24
24
 
25
+ > **Important:** The second argument to `new AppStack(app, "AppStack")` is the stack ID used by CloudFormation. This must exactly match the `stack-name` parameter in your GitHub Actions deploy workflows. If you customize this value (e.g., `"MyProjectAppStack"`), update `.github/workflows/deploy-*.yml` accordingly.
26
+
25
27
  ## lib/cdk-app.ts
26
28
 
27
29
  ```typescript