effortless-aws 0.33.0 → 0.34.0

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.d.ts CHANGED
@@ -263,6 +263,12 @@ type HttpResponse = {
263
263
  contentType?: ContentType;
264
264
  /** Response headers (use for custom content-types not covered by contentType) */
265
265
  headers?: Record<string, string>;
266
+ /**
267
+ * Multiple Set-Cookie values. Used by Lambda Function URLs to set multiple cookies
268
+ * in a single response (e.g., session cookie + CloudFront signed cookies).
269
+ * When present, takes precedence over `set-cookie` in `headers`.
270
+ */
271
+ cookies?: string[];
266
272
  /**
267
273
  * Set to `true` to return binary data.
268
274
  * When enabled, `body` must be a base64-encoded string and the response
@@ -317,7 +323,37 @@ type BucketClient = {
317
323
  /** The underlying S3 bucket name */
318
324
  bucketName: string;
319
325
  };
326
+ /**
327
+ * Typed client for a single entity stored as JSON in a bucket.
328
+ * Objects are stored at `{entityName}/{id}.json`.
329
+ */
330
+ type StoreEntityClient<T> = {
331
+ /** Store a JSON document by id */
332
+ put(id: string, data: T): Promise<void>;
333
+ /** Retrieve a JSON document by id. Returns undefined if not found. */
334
+ get(id: string): Promise<T | undefined>;
335
+ /** Delete a document by id */
336
+ delete(id: string): Promise<void>;
337
+ /** List all documents for this entity */
338
+ list(): Promise<{
339
+ id: string;
340
+ data: T;
341
+ }[]>;
342
+ };
343
+ /**
344
+ * BucketClient extended with typed entity clients.
345
+ */
346
+ type BucketClientWithEntities<Entities extends Record<string, any>> = BucketClient & {
347
+ [K in keyof Entities]: StoreEntityClient<Entities[K]>;
348
+ };
320
349
 
350
+ /**
351
+ * Per-entity configuration for typed JSON key-value storage within a bucket.
352
+ */
353
+ type BucketEntityConfig = {
354
+ /** Cache duration for CloudFront/browser caching (e.g., "10s", "5m", "1h"). No caching if omitted. */
355
+ cache?: Duration;
356
+ };
321
357
  /**
322
358
  * Configuration options for defineBucket.
323
359
  */
@@ -333,6 +369,12 @@ type BucketConfig = {
333
369
  prefix?: string;
334
370
  /** S3 key suffix filter for event notifications (e.g., ".jpg") */
335
371
  suffix?: string;
372
+ /** Typed JSON entity definitions for key-value storage */
373
+ entities?: Record<string, BucketEntityConfig>;
374
+ /** Local directory to seed into bucket on deploy (only uploads files that don't already exist) */
375
+ seed?: string;
376
+ /** Local directory to sync into bucket on every deploy (uploads new/changed, deletes removed) */
377
+ sync?: string;
336
378
  };
337
379
  /**
338
380
  * S3 event record passed to onObjectCreated/onObjectRemoved callbacks.
@@ -354,8 +396,8 @@ type BucketEvent = {
354
396
  /** Spread ctx into callback args (empty when no setup) */
355
397
  type SpreadCtx$6<C> = [C] extends [undefined] ? {} : C & {};
356
398
  /** Setup factory — receives bucket/deps/config/files based on what was declared */
357
- type SetupArgs$6<D, P, HasFiles extends boolean> = {
358
- bucket: BucketClient;
399
+ type SetupArgs$6<D, P, HasFiles extends boolean, Entities extends Record<string, any> = {}> = {
400
+ bucket: {} extends Entities ? BucketClient : BucketClientWithEntities<Entities>;
359
401
  } & ([D] extends [undefined] ? {} : {
360
402
  deps: ResolveDeps<D>;
361
403
  }) & ([P] extends [undefined] ? {} : {
@@ -379,7 +421,7 @@ type BucketObjectRemovedFn<C = undefined> = (args: {
379
421
  * Internal handler object created by defineBucket
380
422
  * @internal
381
423
  */
382
- type BucketHandler$1<C = any> = {
424
+ type BucketHandler$1<C = any, Entities extends Record<string, any> = {}> = {
383
425
  readonly __brand: "effortless-bucket";
384
426
  readonly __spec: BucketConfig;
385
427
  readonly onError?: (...args: any[]) => any;
@@ -397,32 +439,40 @@ type BucketOptions = {
397
439
  prefix?: string;
398
440
  /** S3 key suffix filter for event notifications (e.g., ".jpg") */
399
441
  suffix?: string;
442
+ /** Local directory to seed into bucket on deploy (only uploads files that don't already exist) */
443
+ seed?: string;
444
+ /** Local directory to sync into bucket on every deploy (uploads new/changed, deletes removed) */
445
+ sync?: string;
400
446
  };
401
- interface BucketBuilder<D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false> {
447
+ interface BucketBuilder<D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false, Entities extends Record<string, any> = {}> {
402
448
  /** Declare handler dependencies */
403
- deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): BucketBuilder<D2, P, C, HasFiles>;
449
+ deps<D2 extends Record<string, AnyDepHandler>>(fn: () => D2): BucketBuilder<D2, P, C, HasFiles, Entities>;
404
450
  /** Declare SSM secrets */
405
- config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): BucketBuilder<D, P2, C, HasFiles>;
451
+ config<P2 extends Record<string, AnySecretRef>>(fn: ConfigFactory<P2>): BucketBuilder<D, P2, C, HasFiles, Entities>;
406
452
  /** Include static files in the Lambda ZIP */
407
- include(glob: string): BucketBuilder<D, P, C, true>;
453
+ include(glob: string): BucketBuilder<D, P, C, true, Entities>;
454
+ /** Register a typed JSON entity stored as `{name}/{id}.json` in the bucket */
455
+ entity<N extends string, T>(name: N, options?: BucketEntityConfig): BucketBuilder<D, P, C, HasFiles, Entities & {
456
+ [K in N]: T;
457
+ }>;
408
458
  /** Initialize shared state on cold start with lambda options */
409
- setup(lambda: LambdaOptions): BucketBuilder<D, P, C, HasFiles>;
459
+ setup(lambda: LambdaOptions): BucketBuilder<D, P, C, HasFiles, Entities>;
410
460
  /** Initialize shared state on cold start. Receives bucket (self-client), deps, config, files. */
411
- setup<C2>(fn: (args: SetupArgs$6<D, P, HasFiles>) => C2 | Promise<C2>): BucketBuilder<D, P, C2, HasFiles>;
461
+ setup<C2>(fn: (args: SetupArgs$6<D, P, HasFiles, Entities>) => C2 | Promise<C2>): BucketBuilder<D, P, C2, HasFiles, Entities>;
412
462
  /** Initialize shared state on cold start with lambda options. Receives bucket (self-client), deps, config, files. */
413
- setup<C2>(fn: (args: SetupArgs$6<D, P, HasFiles>) => C2 | Promise<C2>, lambda: LambdaOptions): BucketBuilder<D, P, C2, HasFiles>;
463
+ setup<C2>(fn: (args: SetupArgs$6<D, P, HasFiles, Entities>) => C2 | Promise<C2>, lambda: LambdaOptions): BucketBuilder<D, P, C2, HasFiles, Entities>;
414
464
  /** Handle errors thrown by callbacks */
415
465
  onError(fn: (args: {
416
466
  error: unknown;
417
- } & SpreadCtx$6<C>) => void | Promise<void>): BucketBuilder<D, P, C, HasFiles>;
467
+ } & SpreadCtx$6<C>) => void | Promise<void>): BucketBuilder<D, P, C, HasFiles, Entities>;
418
468
  /** Cleanup callback — runs after each invocation, before Lambda freezes */
419
- onCleanup(fn: (args: SpreadCtx$6<C>) => void | Promise<void>): BucketBuilder<D, P, C, HasFiles>;
469
+ onCleanup(fn: (args: SpreadCtx$6<C>) => void | Promise<void>): BucketBuilder<D, P, C, HasFiles, Entities>;
420
470
  /** Handle S3 ObjectCreated events (terminal — returns finalized handler) */
421
- onObjectCreated(fn: BucketObjectCreatedFn<C>): BucketHandler$1<C>;
471
+ onObjectCreated(fn: BucketObjectCreatedFn<C>): BucketHandler$1<C, Entities>;
422
472
  /** Handle S3 ObjectRemoved events (terminal — returns finalized handler) */
423
- onObjectRemoved(fn: BucketObjectRemovedFn<C>): BucketHandler$1<C>;
473
+ onObjectRemoved(fn: BucketObjectRemovedFn<C>): BucketHandler$1<C, Entities>;
424
474
  /** Finalize as resource-only bucket (no Lambda) */
425
- build(): BucketHandler$1<C>;
475
+ build(): BucketHandler$1<C, Entities>;
426
476
  }
427
477
  /**
428
478
  * Define an S3 bucket with optional event handlers.
@@ -926,10 +976,10 @@ type WorkerClient<T = unknown> = {
926
976
  };
927
977
 
928
978
  /** Dep value types supported by the deps declaration */
929
- type AnyDepHandler = TableHandler$1<any, any> | BucketHandler$1<any> | MailerHandler | FifoQueueHandler$1<any, any> | WorkerHandler$1<any, any>;
979
+ type AnyDepHandler = TableHandler$1<any, any> | BucketHandler$1<any, any> | MailerHandler | FifoQueueHandler$1<any, any> | WorkerHandler$1<any, any>;
930
980
  /** Maps a deps declaration to resolved runtime client types */
931
981
  type ResolveDeps<D> = {
932
- [K in keyof D]: D[K] extends TableHandler$1<infer T> ? TableClient<T> : D[K] extends BucketHandler$1 ? BucketClient : D[K] extends MailerHandler ? EmailClient : D[K] extends FifoQueueHandler$1<infer T> ? QueueClient<T> : D[K] extends WorkerHandler$1<infer T> ? WorkerClient<T> : never;
982
+ [K in keyof D]: D[K] extends TableHandler$1<infer T> ? TableClient<T> : D[K] extends BucketHandler$1<any, infer E> ? ({} extends E ? BucketClient : BucketClientWithEntities<E>) : D[K] extends MailerHandler ? EmailClient : D[K] extends FifoQueueHandler$1<infer T> ? QueueClient<T> : D[K] extends WorkerHandler$1<infer T> ? WorkerClient<T> : never;
933
983
  };
934
984
 
935
985
  /** DynamoDB Streams view type - determines what data is captured in stream records */
@@ -1160,6 +1210,18 @@ declare const defineApp: () => (options: AppConfig) => AppHandler;
1160
1210
  type AnyRoutableHandler = {
1161
1211
  readonly __brand: string;
1162
1212
  };
1213
+ /** Route configuration for serving bucket content through CloudFront */
1214
+ type BucketRouteConfig = {
1215
+ bucket: {
1216
+ readonly __brand: "effortless-bucket";
1217
+ };
1218
+ /** Access control: "private" requires CloudFront signed cookies, "public" serves openly. Default: "public" */
1219
+ access?: "private" | "public";
1220
+ };
1221
+ /** A route value: either an API handler or a bucket route config */
1222
+ type RouteValue = AnyRoutableHandler | BucketRouteConfig;
1223
+ /** Type guard for bucket route entries */
1224
+ declare const isBucketRoute: (v: unknown) => v is BucketRouteConfig;
1163
1225
  /** Simplified request object passed to middleware */
1164
1226
  type MiddlewareRequest = {
1165
1227
  uri: string;
@@ -1206,10 +1268,10 @@ type StaticSiteConfig = {
1206
1268
  build?: string;
1207
1269
  /** Custom domain name. Accepts a string (same domain for all stages) or a Record mapping stage names to domains (e.g., `{ prod: "example.com", dev: "dev.example.com" }`). Requires an ACM certificate in us-east-1. If the cert also covers www, a 301 redirect from www to non-www is set up automatically. */
1208
1270
  domain?: string | Record<string, string>;
1209
- /** CloudFront route overrides: path patterns forwarded to API Gateway instead of S3.
1210
- * Keys are CloudFront path patterns (e.g., "/api/*"), values are HTTP handlers.
1211
- * Example: `routes: { "/api/*": api }` */
1212
- routes?: Record<string, AnyRoutableHandler>;
1271
+ /** CloudFront route overrides: path patterns forwarded to API Gateway or S3 bucket origins.
1272
+ * Keys are CloudFront path patterns (e.g., "/api/*"), values are HTTP handlers or bucket route configs.
1273
+ * Example: `routes: { "/api/*": api, "/files/*": { bucket: storage, access: "private" } }` */
1274
+ routes?: Record<string, RouteValue>;
1213
1275
  /** Custom 404 error page path relative to `dir` (e.g. "404.html").
1214
1276
  * For non-SPA sites only. If not set, a default page is generated automatically. */
1215
1277
  errorPage?: string;
@@ -1236,17 +1298,27 @@ type StaticSiteHandler = {
1236
1298
  */
1237
1299
  declare const defineStaticSite: () => (options: StaticSiteConfig) => StaticSiteHandler;
1238
1300
 
1301
+ /** Options for CloudFront signed cookie policy */
1302
+ type CdnPolicyOptions = {
1303
+ /** Path pattern to grant access to (e.g., "/files/users/123/*"). Supports `*` and `?` wildcards. */
1304
+ path: string;
1305
+ /** How long the CDN access is valid (e.g., "1h", "30m") */
1306
+ ttl: Duration;
1307
+ };
1239
1308
  /** Options for creating a session */
1240
1309
  type SessionOptions = {
1241
1310
  expiresIn?: Duration;
1311
+ /** CloudFront signed cookie policy for CDN-level access control */
1312
+ cdnPolicy?: CdnPolicyOptions;
1242
1313
  };
1243
- /** Session response with Set-Cookie header */
1314
+ /** Session response with Set-Cookie headers */
1244
1315
  type SessionResponse = {
1245
1316
  status: 200;
1246
1317
  body: {
1247
1318
  ok: true;
1248
1319
  };
1249
1320
  headers: Record<string, string>;
1321
+ cookies?: string[];
1250
1322
  };
1251
1323
  /**
1252
1324
  * Auth helpers injected into API handler callback args when `auth` is configured.
@@ -1300,12 +1372,28 @@ type OkHelper = (body?: unknown, status?: number) => HttpResponse;
1300
1372
  /** Error response helper: `fail("message")` → `{ status: 400, body: { error: "message" } }` */
1301
1373
  type FailHelper = (message: string, status?: number) => HttpResponse;
1302
1374
  type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
1375
+ /** Cache options for a GET route. Duration shorthand (e.g. "30s", "5m") or object for fine-grained control. */
1376
+ type CacheOptions = Duration | {
1377
+ ttl: Duration;
1378
+ swr?: Duration;
1379
+ scope?: "public" | "private";
1380
+ };
1381
+ /** Resolved cache config with numeric seconds */
1382
+ type ResolvedCache = {
1383
+ private?: false;
1384
+ ttl: number;
1385
+ swr: number;
1386
+ } | {
1387
+ private: true;
1388
+ ttl: number;
1389
+ };
1303
1390
  /** Parsed route definition stored at runtime */
1304
1391
  type RouteEntry = {
1305
1392
  method: HttpMethod;
1306
1393
  path: string;
1307
1394
  onRequest: (...args: any[]) => any;
1308
1395
  public?: boolean;
1396
+ cache?: ResolvedCache;
1309
1397
  };
1310
1398
  /** Spread ctx into route args: Omit auth config, add AuthHelpers if present */
1311
1399
  type SpreadCtx$2<C> = ([C] extends [undefined] ? {} : Omit<C & {}, 'auth'>) & ([ExtractAuth<C>] extends [undefined] ? {} : {
@@ -1322,10 +1410,14 @@ type RouteArgs<C, ST> = SpreadCtx$2<C> & {
1322
1410
  } : {});
1323
1411
  /** Route handler function */
1324
1412
  type RouteHandler<C, ST> = (args: RouteArgs<C, ST>) => Promise<HttpResponse | void> | HttpResponse | void;
1325
- /** Route options (e.g. public) */
1413
+ /** Route options for all methods */
1326
1414
  type RouteOptions = {
1327
1415
  public?: boolean;
1328
1416
  };
1417
+ /** Route options for GET — supports caching */
1418
+ type GetRouteOptions = RouteOptions & {
1419
+ cache?: CacheOptions;
1420
+ };
1329
1421
  /** Setup factory — receives deps/config/files/enableAuth based on what was declared */
1330
1422
  type SetupArgs$2<D, P, HasFiles extends boolean> = {
1331
1423
  enableAuth: EnableAuth;
@@ -1375,7 +1467,7 @@ type ApiOptions = {
1375
1467
  * Has `__brand` so CLI discovers it. Each `.get()/.post()` adds a route and returns self.
1376
1468
  */
1377
1469
  interface ApiRoutes<C = undefined, ST extends boolean = false> extends ApiHandler<C> {
1378
- get(path: `/${string}`, handler: RouteHandler<C, ST>, options?: RouteOptions): ApiRoutes<C, ST>;
1470
+ get(path: `/${string}`, handler: RouteHandler<C, ST>, options?: GetRouteOptions): ApiRoutes<C, ST>;
1379
1471
  post(path: `/${string}`, handler: RouteHandler<C, ST>, options?: RouteOptions): ApiRoutes<C, ST>;
1380
1472
  put(path: `/${string}`, handler: RouteHandler<C, ST>, options?: RouteOptions): ApiRoutes<C, ST>;
1381
1473
  patch(path: `/${string}`, handler: RouteHandler<C, ST>, options?: RouteOptions): ApiRoutes<C, ST>;
@@ -1410,7 +1502,7 @@ interface ApiBuilder<D = undefined, P = undefined, C = undefined, ST extends boo
1410
1502
  /** Cleanup callback — runs after each invocation, before Lambda freezes */
1411
1503
  onCleanup(fn: (args: SpreadCtx$2<C>) => void | Promise<void>): ApiBuilder<D, P, C, ST, HasFiles>;
1412
1504
  /** Add a GET route (terminal — returns finalized handler with route methods) */
1413
- get(path: `/${string}`, handler: RouteHandler<C, ST>, options?: RouteOptions): ApiRoutes<C, ST>;
1505
+ get(path: `/${string}`, handler: RouteHandler<C, ST>, options?: GetRouteOptions): ApiRoutes<C, ST>;
1414
1506
  /** Add a POST route (terminal) */
1415
1507
  post(path: `/${string}`, handler: RouteHandler<C, ST>, options?: RouteOptions): ApiRoutes<C, ST>;
1416
1508
  /** Add a PUT route (terminal) */
@@ -1565,6 +1657,8 @@ type McpConfig = {
1565
1657
  name: string;
1566
1658
  /** MCP server version (default: "1.0.0") */
1567
1659
  version?: string;
1660
+ /** Human-readable description — sent to clients in initialize response as system prompt context */
1661
+ instructions?: string;
1568
1662
  /** Lambda function settings (memory, timeout, permissions, etc.) */
1569
1663
  lambda?: LambdaWithPermissions;
1570
1664
  };
@@ -1595,8 +1689,93 @@ type McpToolResult = {
1595
1689
  content: McpToolContent[];
1596
1690
  isError?: boolean;
1597
1691
  };
1692
+ /** Content returned when reading a resource */
1693
+ type McpResourceContent = {
1694
+ uri: string;
1695
+ mimeType?: string;
1696
+ text: string;
1697
+ } | {
1698
+ uri: string;
1699
+ mimeType?: string;
1700
+ blob: string;
1701
+ };
1702
+ /** A static resource definition */
1703
+ type McpResourceDef$1<C = undefined> = {
1704
+ /** Human-readable name */
1705
+ name: string;
1706
+ /** Optional description */
1707
+ description?: string;
1708
+ /** Optional MIME type */
1709
+ mimeType?: string;
1710
+ /** Handler called on resources/read */
1711
+ handler: (ctx: SpreadCtx<C>) => McpResourceContent | McpResourceContent[] | Promise<McpResourceContent | McpResourceContent[]>;
1712
+ };
1713
+ /** A parameterized resource template (URI template RFC 6570) */
1714
+ type McpResourceTemplateDef$1<C = undefined> = {
1715
+ /** Human-readable name */
1716
+ name: string;
1717
+ /** Optional description */
1718
+ description?: string;
1719
+ /** Optional MIME type */
1720
+ mimeType?: string;
1721
+ /** Handler called on resources/read — receives matched URI params */
1722
+ handler: (params: Record<string, string>, ctx: SpreadCtx<C>) => McpResourceContent | McpResourceContent[] | Promise<McpResourceContent | McpResourceContent[]>;
1723
+ };
1724
+ /** Map of uri → resource definition, or uriTemplate → template definition */
1725
+ type McpResourceMap$1<C = undefined> = {
1726
+ [uriOrTemplate: string]: McpResourceDef$1<C> | McpResourceTemplateDef$1<C>;
1727
+ };
1728
+ /** An argument accepted by a prompt */
1729
+ type McpPromptArgument = {
1730
+ /** Argument name */
1731
+ name: string;
1732
+ /** Optional description */
1733
+ description?: string;
1734
+ /** Whether the argument is required (default: false) */
1735
+ required?: boolean;
1736
+ };
1737
+ /** Content block inside a prompt message */
1738
+ type McpPromptContent = {
1739
+ type: "text";
1740
+ text: string;
1741
+ } | {
1742
+ type: "image";
1743
+ data: string;
1744
+ mimeType: string;
1745
+ } | {
1746
+ type: "audio";
1747
+ data: string;
1748
+ mimeType: string;
1749
+ } | {
1750
+ type: "resource";
1751
+ resource: {
1752
+ uri: string;
1753
+ mimeType?: string;
1754
+ text?: string;
1755
+ blob?: string;
1756
+ };
1757
+ };
1758
+ /** A single message returned by a prompt */
1759
+ type McpPromptMessage = {
1760
+ role: "user" | "assistant";
1761
+ content: McpPromptContent;
1762
+ };
1763
+ /** Result returned by a prompt handler */
1764
+ type McpPromptResult = {
1765
+ description?: string;
1766
+ messages: McpPromptMessage[];
1767
+ };
1768
+ /** A single prompt definition */
1769
+ type McpPromptDef$1<C = undefined> = {
1770
+ /** Human-readable description */
1771
+ description?: string;
1772
+ /** Arguments this prompt accepts */
1773
+ arguments?: McpPromptArgument[];
1774
+ /** Handler called on prompts/get — receives argument values */
1775
+ handler: (args: Record<string, string>, ctx: SpreadCtx<C>) => McpPromptResult | Promise<McpPromptResult>;
1776
+ };
1598
1777
  /** A single MCP tool definition */
1599
- type McpToolDef<C = undefined> = {
1778
+ type McpToolDef$1<C = undefined> = {
1600
1779
  /** Human-readable description of the tool */
1601
1780
  description: string;
1602
1781
  /** JSON Schema describing the tool's input parameters */
@@ -1604,8 +1783,10 @@ type McpToolDef<C = undefined> = {
1604
1783
  /** Handler function called when the tool is invoked */
1605
1784
  handler: (input: any, ctx: SpreadCtx<C>) => McpToolResult | Promise<McpToolResult>;
1606
1785
  };
1607
- /** Setup factory — receives deps/config/files based on what was declared */
1608
- type SetupArgs<D, P, HasFiles extends boolean> = ([D] extends [undefined] ? {} : {
1786
+ /** Setup factory — receives deps/config/files/enableAuth based on what was declared */
1787
+ type SetupArgs<D, P, HasFiles extends boolean> = {
1788
+ enableAuth: EnableAuth;
1789
+ } & ([D] extends [undefined] ? {} : {
1609
1790
  deps: ResolveDeps<D>;
1610
1791
  }) & ([P] extends [undefined] ? {} : {
1611
1792
  config: ResolveConfig<P & {}>;
@@ -1627,6 +1808,8 @@ type McpHandler$1<C = any> = {
1627
1808
  readonly deps?: Record<string, unknown> | (() => Record<string, unknown>);
1628
1809
  readonly config?: Record<string, unknown>;
1629
1810
  readonly static?: string[];
1811
+ readonly resources?: (...args: any[]) => any;
1812
+ readonly prompts?: (...args: any[]) => any;
1630
1813
  readonly tools?: (...args: any[]) => any;
1631
1814
  };
1632
1815
  /** Options passed to `defineMcp()` */
@@ -1635,6 +1818,8 @@ type McpOptions = {
1635
1818
  name: string;
1636
1819
  /** MCP server version (default: "1.0.0") */
1637
1820
  version?: string;
1821
+ /** Human-readable description — sent to clients in initialize response as system prompt context */
1822
+ instructions?: string;
1638
1823
  };
1639
1824
  interface McpBuilder<D = undefined, P = undefined, C = undefined, HasFiles extends boolean = false> {
1640
1825
  /** Declare handler dependencies (tables, queues, buckets, mailers, workers) */
@@ -1656,38 +1841,35 @@ interface McpBuilder<D = undefined, P = undefined, C = undefined, HasFiles exten
1656
1841
  } & SpreadCtx<C>) => void | Promise<void>): McpBuilder<D, P, C, HasFiles>;
1657
1842
  /** Cleanup callback — runs on shutdown */
1658
1843
  onCleanup(fn: (args: SpreadCtx<C>) => void | Promise<void>): McpBuilder<D, P, C, HasFiles>;
1844
+ /** Define MCP resources (chainable) */
1845
+ resources(fn: (ctx: SpreadCtx<C>) => McpResourceMap$1<C>): McpBuilder<D, P, C, HasFiles>;
1846
+ /** Define MCP prompts (chainable) */
1847
+ prompts(fn: (ctx: SpreadCtx<C>) => Record<string, McpPromptDef$1<C>>): McpBuilder<D, P, C, HasFiles>;
1659
1848
  /** Define MCP tools (terminal) */
1660
- tools(fn: (ctx: SpreadCtx<C>) => Record<string, McpToolDef<C>>): McpHandler$1<C>;
1849
+ tools(fn: (ctx: SpreadCtx<C>) => Record<string, McpToolDef$1<C>>): McpHandler$1<C>;
1661
1850
  }
1662
1851
  /**
1663
1852
  * Define an MCP (Model Context Protocol) server endpoint.
1664
1853
  *
1665
- * Creates a Lambda-backed MCP server that exposes tools for AI models
1666
- * and MCP-compatible clients to discover and invoke.
1854
+ * Creates a Lambda-backed MCP server that exposes tools, resources, and prompts
1855
+ * for AI models and MCP-compatible clients via Streamable HTTP (JSON-RPC over POST).
1667
1856
  *
1668
- * @example
1669
- * ```typescript
1670
- * export const mcp = defineMcp({ name: "my-tools" })
1671
- * .deps(() => ({ users: usersTable }))
1672
- * .setup(({ deps }) => ({ db: deps.users }))
1673
- * .tools(({ db }) => ({
1674
- * get_user: {
1675
- * description: "Get a user by ID",
1676
- * input: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
1677
- * handler: async (input) => ({
1678
- * content: [{ type: "text", text: JSON.stringify(await db.get({ pk: input.id, sk: "profile" })) }]
1679
- * })
1680
- * }
1681
- * }))
1682
- * ```
1857
+ * @see https://modelcontextprotocol.io/specification/2025-03-26 — MCP specification
1858
+ * @see https://effortless-aws.com/use-cases/mcp-server/ — full documentation with examples
1683
1859
  */
1684
1860
  declare function defineMcp(options: McpOptions): McpBuilder;
1685
1861
 
1862
+ type McpToolDef = McpToolDef$1;
1863
+ type McpResourceDef = McpResourceDef$1;
1864
+ type McpResourceTemplateDef = McpResourceTemplateDef$1;
1865
+ type McpResourceMap = McpResourceMap$1;
1866
+ type McpPromptDef = McpPromptDef$1;
1867
+
1686
1868
  type TableHandler<T = Record<string, unknown>> = TableHandler$1<T, any>;
1687
1869
  type FifoQueueHandler<T = unknown> = FifoQueueHandler$1<T, any>;
1688
- type BucketHandler = BucketHandler$1<any>;
1870
+ type BucketHandler<Entities extends Record<string, any> = {}> = BucketHandler$1<any, Entities>;
1689
1871
  type CronHandler = CronHandler$1<any>;
1690
1872
  type WorkerHandler<T = any> = WorkerHandler$1<T, any>;
1691
1873
  type McpHandler = McpHandler$1<any>;
1692
1874
 
1693
- export { type ApiAuthConfig, type ApiConfig, type ApiHandler, type ApiRoutes, type AppConfig, type AppHandler, type AuthHelpers, type BucketClient, type BucketConfig, type BucketEvent, type BucketHandler, type ConfigHelpers, type ContentType, type CronConfig, type CronHandler, type DefineSecretFn, type Duration, type EffortlessConfig, type EmailClient, type FifoQueueConfig, type FifoQueueHandler, type FifoQueueMessage, type HttpMethod$1 as HttpMethod, type HttpRequest, type HttpResponse, type LogLevel, type MailerConfig, type MailerHandler, type McpConfig, type McpHandler, type McpInputSchema, type McpToolContent, type McpToolDef, type McpToolResult, type MiddlewareDeny, type MiddlewareHandler, type MiddlewareRedirect, type MiddlewareRequest, type MiddlewareResult, type ParamRef, type Permission, type PutInput, type PutOptions, type QueryByTagParams, type QueryParams, type QueueClient, type ResponseStream, type SecretRef, type SendEmailOptions, type SendMessageInput, type SkCondition, type StaticFiles, type StaticSiteConfig, type StaticSiteHandler, type StaticSiteSeo, type StreamView, type TableClient, type TableConfig, type TableHandler, type TableItem, type TableKey, type TableRecord, type Timezone, type UpdateActions, type WorkerClient, type WorkerConfig, type WorkerHandler, type WorkerSendOptions, defineApi, defineApp, defineBucket, defineConfig, defineCron, defineFifoQueue, defineMailer, defineMcp, defineSecret, defineStaticSite, defineTable, defineWorker, generateBase64, generateHex, generateUuid, param, secret, toSeconds };
1875
+ export { type ApiAuthConfig, type ApiConfig, type ApiHandler, type ApiRoutes, type AppConfig, type AppHandler, type AuthHelpers, type BucketClient, type BucketClientWithEntities, type BucketConfig, type BucketEntityConfig, type BucketEvent, type BucketHandler, type BucketRouteConfig, type CacheOptions, type CdnPolicyOptions, type ConfigHelpers, type ContentType, type CronConfig, type CronHandler, type DefineSecretFn, type Duration, type EffortlessConfig, type EmailClient, type FifoQueueConfig, type FifoQueueHandler, type FifoQueueMessage, type HttpMethod$1 as HttpMethod, type HttpRequest, type HttpResponse, type LogLevel, type MailerConfig, type MailerHandler, type McpConfig, type McpHandler, type McpInputSchema, type McpPromptArgument, type McpPromptContent, type McpPromptDef, type McpPromptMessage, type McpPromptResult, type McpResourceContent, type McpResourceDef, type McpResourceMap, type McpResourceTemplateDef, type McpToolContent, type McpToolDef, type McpToolResult, type MiddlewareDeny, type MiddlewareHandler, type MiddlewareRedirect, type MiddlewareRequest, type MiddlewareResult, type ParamRef, type Permission, type PutInput, type PutOptions, type QueryByTagParams, type QueryParams, type QueueClient, type ResponseStream, type SecretRef, type SendEmailOptions, type SendMessageInput, type SkCondition, type StaticFiles, type StaticSiteConfig, type StaticSiteHandler, type StaticSiteSeo, type StoreEntityClient, type StreamView, type TableClient, type TableConfig, type TableHandler, type TableItem, type TableKey, type TableRecord, type Timezone, type UpdateActions, type WorkerClient, type WorkerConfig, type WorkerHandler, type WorkerSendOptions, defineApi, defineApp, defineBucket, defineConfig, defineCron, defineFifoQueue, defineMailer, defineMcp, defineSecret, defineStaticSite, defineTable, defineWorker, generateBase64, generateHex, generateUuid, isBucketRoute, param, secret, toSeconds };
package/dist/index.js CHANGED
@@ -116,6 +116,7 @@ var defineApp = () => (options) => ({
116
116
  });
117
117
 
118
118
  // src/handlers/define-static-site.ts
119
+ var isBucketRoute = (v) => v != null && typeof v === "object" && "bucket" in v && v.bucket != null && typeof v.bucket === "object" && v.bucket.__brand === "effortless-bucket";
119
120
  var defineStaticSite = () => (options) => ({
120
121
  __brand: "effortless-static-site",
121
122
  __spec: options
@@ -231,6 +232,13 @@ function defineBucket(options) {
231
232
  state.static = [...state.static ?? [], glob];
232
233
  return builder;
233
234
  },
235
+ entity(name, options2) {
236
+ state.spec = {
237
+ ...state.spec,
238
+ entities: { ...state.spec.entities, [name]: options2 ?? {} }
239
+ };
240
+ return builder;
241
+ },
234
242
  setup(fnOrLambda, maybeLambda) {
235
243
  if (typeof fnOrLambda === "function") {
236
244
  state.setup = fnOrLambda;
@@ -270,6 +278,20 @@ var defineMailer = () => (options) => ({
270
278
  });
271
279
 
272
280
  // src/handlers/define-api.ts
281
+ var resolveCache = (cache) => {
282
+ if (typeof cache === "number" || typeof cache === "string") {
283
+ const ttl2 = toSeconds(cache);
284
+ return { ttl: ttl2, swr: ttl2 * 2 };
285
+ }
286
+ const ttl = toSeconds(cache.ttl);
287
+ if (cache.scope === "private") {
288
+ return { private: true, ttl };
289
+ }
290
+ return {
291
+ ttl,
292
+ swr: cache.swr != null ? toSeconds(cache.swr) : ttl * 2
293
+ };
294
+ };
273
295
  function defineApi(options) {
274
296
  const { basePath, stream } = options;
275
297
  const state = {
@@ -280,11 +302,13 @@ function defineApi(options) {
280
302
  routes: []
281
303
  };
282
304
  const addRoute = (method, path, handler, opts) => {
305
+ const routeCache = opts?.cache != null ? resolveCache(opts.cache) : void 0;
283
306
  state.routes.push({
284
307
  method,
285
308
  path,
286
309
  onRequest: handler,
287
- ...opts?.public ? { public: true } : {}
310
+ ...opts?.public ? { public: true } : {},
311
+ ...routeCache ? { cache: routeCache } : {}
288
312
  });
289
313
  };
290
314
  const applyLambdaOptions = (lambda) => {
@@ -495,7 +519,8 @@ function defineWorker(options) {
495
519
  function defineMcp(options) {
496
520
  const spec = {
497
521
  name: options.name,
498
- ...options.version ? { version: options.version } : {}
522
+ ...options.version ? { version: options.version } : {},
523
+ ...options.instructions ? { instructions: options.instructions } : {}
499
524
  };
500
525
  const state = { spec };
501
526
  const applyLambdaOptions = (lambda) => {
@@ -512,6 +537,8 @@ function defineMcp(options) {
512
537
  ...state.deps ? { deps: state.deps } : {},
513
538
  ...state.config ? { config: state.config } : {},
514
539
  ...state.static ? { static: state.static } : {},
540
+ ...state.resources ? { resources: state.resources } : {},
541
+ ...state.prompts ? { prompts: state.prompts } : {},
515
542
  ...state.tools ? { tools: state.tools } : {}
516
543
  });
517
544
  const builder = {
@@ -544,6 +571,14 @@ function defineMcp(options) {
544
571
  state.onCleanup = fn;
545
572
  return builder;
546
573
  },
574
+ resources(fn) {
575
+ state.resources = fn;
576
+ return builder;
577
+ },
578
+ prompts(fn) {
579
+ state.prompts = fn;
580
+ return builder;
581
+ },
547
582
  tools(fn) {
548
583
  state.tools = fn;
549
584
  return finalize();
@@ -567,6 +602,7 @@ export {
567
602
  generateBase64,
568
603
  generateHex,
569
604
  generateUuid,
605
+ isBucketRoute,
570
606
  param,
571
607
  secret,
572
608
  toSeconds