@jaypie/mcp 0.2.7 → 0.2.9

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.7#8fb79282"
888
+ const BUILD_VERSION_STRING = "@jaypie/mcp@0.2.9#f022ac30"
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.7",
3
+ "version": "0.2.9",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -102,54 +102,52 @@ Preconfigured with API-optimized timeouts and role tags.
102
102
 
103
103
  ### Streaming Lambda Functions
104
104
 
105
- Enable Lambda Response Streaming via Function URLs for real-time SSE responses:
105
+ Use `JaypieStreamingLambda` for Express apps with AWS Lambda Web Adapter and streaming support:
106
106
 
107
107
  ```typescript
108
- import { FunctionUrlAuthType, InvokeMode } from "aws-cdk-lib/aws-lambda";
109
-
110
- const streamingLambda = new JaypieLambda(this, "StreamingFunction", {
111
- code: "dist",
112
- handler: "stream.handler",
113
- timeout: Duration.minutes(5), // Longer timeout for streaming
108
+ import { JaypieStreamingLambda, JaypieDistribution } from "@jaypie/constructs";
109
+
110
+ // Create streaming Lambda with Web Adapter
111
+ const streamingLambda = new JaypieStreamingLambda(this, "StreamingApi", {
112
+ code: "dist/api",
113
+ handler: "run.sh", // Shell script to start Express app
114
+ streaming: true, // Enables RESPONSE_STREAM invoke mode
115
+ port: 8000, // Port your app listens on (default: 8000)
114
116
  });
115
117
 
116
- // Add Function URL with streaming enabled
117
- const functionUrl = streamingLambda.addFunctionUrl({
118
- authType: FunctionUrlAuthType.NONE, // Public access
119
- // authType: FunctionUrlAuthType.AWS_IAM, // IAM authentication
120
- invokeMode: InvokeMode.RESPONSE_STREAM, // Enable streaming
121
- });
122
-
123
- // Output the URL
124
- new cdk.CfnOutput(this, "StreamingUrl", {
125
- value: functionUrl.url,
118
+ // CloudFront auto-detects invokeMode from JaypieStreamingLambda
119
+ new JaypieDistribution(this, "Distribution", {
120
+ handler: streamingLambda,
121
+ host: "api.example.com",
122
+ zone: "example.com",
126
123
  });
127
124
  ```
128
125
 
129
126
  Features:
130
- - `RESPONSE_STREAM` invoke mode enables real-time streaming
131
- - Works with `lambdaStreamHandler` from `@jaypie/lambda`
132
- - Use `FunctionUrlAuthType.AWS_IAM` for authenticated endpoints
133
- - Combine with API Gateway for custom domains via `JaypieApiGateway`
127
+ - Automatically adds AWS Lambda Web Adapter layer (supports ARM64 and x86_64)
128
+ - Sets `AWS_LAMBDA_EXEC_WRAPPER` and `AWS_LWA_INVOKE_MODE` environment variables
129
+ - Exposes `invokeMode` property for auto-detection by `JaypieDistribution`
130
+ - Use `streaming: false` for buffered mode (traditional Lambda behavior)
134
131
 
135
- For Express-based streaming with custom domains:
132
+ For direct Function URL access (bypass CloudFront):
136
133
  ```typescript
137
- // Express app with streaming routes
138
- const expressLambda = new JaypieExpressLambda(this, "ExpressStream", {
134
+ import { FunctionUrlAuthType, InvokeMode } from "aws-cdk-lib/aws-lambda";
135
+
136
+ const streamingLambda = new JaypieLambda(this, "StreamingFunction", {
139
137
  code: "dist",
140
- handler: "app.handler",
138
+ handler: "stream.handler",
141
139
  timeout: Duration.minutes(5),
142
140
  });
143
141
 
144
- // API Gateway handles the domain routing
145
- new JaypieApiGateway(this, "Api", {
146
- handler: expressLambda,
147
- host: "api.example.com",
148
- zone: "example.com",
142
+ const functionUrl = streamingLambda.addFunctionUrl({
143
+ authType: FunctionUrlAuthType.NONE,
144
+ invokeMode: InvokeMode.RESPONSE_STREAM,
149
145
  });
146
+
147
+ new cdk.CfnOutput(this, "StreamingUrl", { value: functionUrl.url });
150
148
  ```
151
149
 
152
- Note: API Gateway has a 29-second timeout limit. For longer streaming operations, use Function URLs directly.
150
+ Note: API Gateway has a 29-second timeout limit. For longer streaming operations, use Function URLs or CloudFront with Lambda Function URLs.
153
151
 
154
152
  ### Stack Types
155
153
 
@@ -430,6 +430,99 @@ try {
430
430
  }
431
431
  ```
432
432
 
433
+ ## Seed and Export Utilities
434
+
435
+ Idempotent seeding and data export for migrations and bootstrapping.
436
+
437
+ ### seedEntityIfNotExists
438
+
439
+ Seed a single entity if it doesn't already exist (checked by alias):
440
+
441
+ ```typescript
442
+ import { APEX, seedEntityIfNotExists } from "@jaypie/dynamodb";
443
+
444
+ const created = await seedEntityIfNotExists({
445
+ alias: "config-main",
446
+ model: "config",
447
+ name: "Main Configuration",
448
+ ou: APEX,
449
+ });
450
+ // Returns true if created, false if already exists
451
+ ```
452
+
453
+ ### seedEntities
454
+
455
+ Seed multiple entities with idempotency:
456
+
457
+ ```typescript
458
+ import { APEX, seedEntities } from "@jaypie/dynamodb";
459
+
460
+ const result = await seedEntities([
461
+ { alias: "vocab-en", model: "vocabulary", name: "English", ou: APEX },
462
+ { alias: "vocab-es", model: "vocabulary", name: "Spanish", ou: APEX },
463
+ ]);
464
+ // result.created: aliases of created entities
465
+ // result.skipped: aliases of entities that already existed
466
+ // result.errors: { alias, error } for failed operations
467
+
468
+ // Dry run (preview without writing)
469
+ const preview = await seedEntities(entities, { dryRun: true });
470
+
471
+ // Replace existing entities
472
+ await seedEntities(entities, { replace: true });
473
+ ```
474
+
475
+ Auto-generates `id` (UUID), `createdAt`, `updatedAt`, and `sequence` if missing.
476
+
477
+ ### exportEntities
478
+
479
+ Export entities by model and organizational unit:
480
+
481
+ ```typescript
482
+ import { APEX, exportEntities } from "@jaypie/dynamodb";
483
+
484
+ const { entities, count } = await exportEntities("vocabulary", APEX);
485
+ // entities: FabricEntity[] sorted by sequence ascending
486
+ // count: number of entities
487
+
488
+ // With limit
489
+ const { entities: limited } = await exportEntities("vocabulary", APEX, 100);
490
+ ```
491
+
492
+ ### exportEntitiesToJson
493
+
494
+ Export as JSON string:
495
+
496
+ ```typescript
497
+ import { APEX, exportEntitiesToJson } from "@jaypie/dynamodb";
498
+
499
+ const json = await exportEntitiesToJson("vocabulary", APEX);
500
+ // Pretty printed by default
501
+
502
+ const compact = await exportEntitiesToJson("vocabulary", APEX, false);
503
+ // Compact JSON
504
+ ```
505
+
506
+ ### SeedResult Interface
507
+
508
+ ```typescript
509
+ interface SeedResult {
510
+ created: string[]; // Aliases of created entities
511
+ skipped: string[]; // Aliases of skipped entities (already exist)
512
+ errors: Array<{ alias: string; error: string }>;
513
+ }
514
+
515
+ interface SeedOptions {
516
+ replace?: boolean; // Overwrite existing (default: false)
517
+ dryRun?: boolean; // Preview without writing (default: false)
518
+ }
519
+
520
+ interface ExportResult<T extends FabricEntity = FabricEntity> {
521
+ entities: T[]; // Exported entities
522
+ count: number; // Number of entities
523
+ }
524
+ ```
525
+
433
526
  ## Testing
434
527
 
435
528
  Mock implementations in `@jaypie/testkit`:
@@ -445,9 +538,17 @@ vi.mock("@jaypie/dynamodb", async () => {
445
538
  // Key builders and indexEntity work correctly (delegate to real implementations)
446
539
  // Query functions return empty results by default
447
540
  // Entity operations return sensible defaults
541
+ // Seed functions return { created: [], skipped: [], errors: [] } by default
542
+ // Export functions return { entities: [], count: 0 } by default
448
543
 
449
544
  // Customize mock behavior:
450
- import { queryByOu, getEntity, putEntity } from "@jaypie/testkit/mock";
545
+ import {
546
+ exportEntities,
547
+ getEntity,
548
+ putEntity,
549
+ queryByOu,
550
+ seedEntities,
551
+ } from "@jaypie/testkit/mock";
451
552
 
452
553
  queryByOu.mockResolvedValue({
453
554
  items: [{ id: "123", name: "Test" }],
@@ -455,6 +556,17 @@ queryByOu.mockResolvedValue({
455
556
  });
456
557
 
457
558
  getEntity.mockResolvedValue({ id: "123", model: "record", name: "Test" });
559
+
560
+ seedEntities.mockResolvedValue({
561
+ created: ["entity-1", "entity-2"],
562
+ skipped: [],
563
+ errors: [],
564
+ });
565
+
566
+ exportEntities.mockResolvedValue({
567
+ entities: [{ id: "123", model: "record" }],
568
+ count: 1,
569
+ });
458
570
  ```
459
571
 
460
572
  ## Best Practices
@@ -407,6 +407,50 @@ expressHandler automatically sets these headers:
407
407
 
408
408
  When Datadog environment variables are configured, expressHandler automatically submits metrics for each request including status code and path.
409
409
 
410
+ ## Server Creation
411
+
412
+ Use `createServer` to quickly set up an Express server with standard Jaypie middleware.
413
+
414
+ ### Basic Server Usage
415
+
416
+ ```typescript
417
+ import express from "express";
418
+ import { createServer, expressHandler } from "jaypie";
419
+
420
+ const app = express();
421
+
422
+ app.get("/", expressHandler(async (req, res) => {
423
+ return { message: "Hello World" };
424
+ }));
425
+
426
+ const { server, port } = await createServer(app);
427
+ console.log(`Server running on port ${port}`);
428
+ ```
429
+
430
+ ### Server Options
431
+
432
+ ```typescript
433
+ import { createServer } from "jaypie";
434
+ import type { CreateServerOptions } from "jaypie";
435
+
436
+ const options: CreateServerOptions = {
437
+ port: 3000, // Port to listen on (default: PORT env var or 8080)
438
+ cors: { origin: "*" }, // CORS config (false to disable)
439
+ jsonLimit: "10mb", // JSON body parser limit (default: "1mb")
440
+ middleware: [myMiddleware], // Additional middleware to apply
441
+ };
442
+
443
+ const { server, port } = await createServer(app, options);
444
+ ```
445
+
446
+ ### Server Result
447
+
448
+ ```typescript
449
+ import type { ServerResult } from "jaypie";
450
+
451
+ // { server: Server, port: number }
452
+ ```
453
+
410
454
  ## Streaming Responses
411
455
 
412
456
  Use `expressStreamHandler` for Server-Sent Events (SSE) streaming responses. Ideal for real-time updates, LLM streaming, and long-running operations.
@@ -497,3 +541,33 @@ import type {
497
541
  JaypieStreamHandlerValidate,
498
542
  } from "jaypie";
499
543
  ```
544
+
545
+ ## Invoke UUID Detection
546
+
547
+ Use `getCurrentInvokeUuid` to get the current request ID. Automatically detects the environment (Lambda, Lambda Web Adapter, or local development).
548
+
549
+ ### Basic Usage
550
+
551
+ ```typescript
552
+ import { getCurrentInvokeUuid } from "jaypie";
553
+
554
+ const handler = expressHandler(async (req, res) => {
555
+ const invokeUuid = getCurrentInvokeUuid(req);
556
+ // Returns AWS request ID in Lambda, or generates a local UUID
557
+ return { requestId: invokeUuid };
558
+ });
559
+ ```
560
+
561
+ ### Environment Detection
562
+
563
+ The function adapts to different runtime environments:
564
+
565
+ 1. **Lambda (native)**: Uses `awsRequestId` from Lambda context
566
+ 2. **Lambda Web Adapter**: Extracts from `x-amzn-request-id` header or `_X_AMZN_TRACE_ID` env var
567
+ 3. **Local development**: Generates a UUID for consistent tracing
568
+
569
+ ### Lambda Web Adapter Headers
570
+
571
+ When running Express behind AWS Lambda Web Adapter, the function extracts the request ID from:
572
+ - `x-amzn-request-id` header (set by Lambda Web Adapter)
573
+ - `_X_AMZN_TRACE_ID` environment variable (X-Ray trace ID, set by Lambda runtime)
@@ -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;