@creature-ai/sdk 0.1.6 → 0.1.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.
@@ -1,6 +1,65 @@
1
1
  import { z } from 'zod';
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
 
4
+ /**
5
+ * Interface for external state storage.
6
+ * Implementations can use Redis, DynamoDB, MongoDB, etc.
7
+ */
8
+ interface StateAdapter {
9
+ /**
10
+ * Get state for an instance.
11
+ */
12
+ get<T>(instanceId: string): Promise<T | undefined>;
13
+ /**
14
+ * Set state for an instance.
15
+ */
16
+ set<T>(instanceId: string, state: T): Promise<void>;
17
+ /**
18
+ * Delete state for an instance.
19
+ */
20
+ delete(instanceId: string): Promise<void>;
21
+ }
22
+ /**
23
+ * Interface for external realtime communication.
24
+ * Implementations can use Pusher, Ably, Redis pub/sub, etc.
25
+ */
26
+ interface RealtimeAdapter {
27
+ /**
28
+ * Send a message to all clients subscribed to an instance.
29
+ */
30
+ send<T>(instanceId: string, message: T): Promise<void>;
31
+ /**
32
+ * Subscribe to messages for an instance.
33
+ */
34
+ subscribe(instanceId: string, handler: (message: unknown) => void): () => void;
35
+ /**
36
+ * Get the WebSocket/channel URL for clients to connect.
37
+ */
38
+ getWebSocketUrl(instanceId: string): string;
39
+ }
40
+ /**
41
+ * Options for configuring adapters.
42
+ */
43
+ interface AdapterOptions {
44
+ /** External state adapter (for stateful features in serverless). */
45
+ stateAdapter?: StateAdapter;
46
+ /** External realtime adapter (for WebSocket-like features in serverless). */
47
+ realtimeAdapter?: RealtimeAdapter;
48
+ }
49
+ /**
50
+ * Options for vercelMcp adapter.
51
+ */
52
+ interface VercelMcpOptions extends AdapterOptions {
53
+ /** Base path for MCP routes (default: '/api'). */
54
+ basePath?: string;
55
+ /** Maximum duration for serverless function (default: 60). */
56
+ maxDuration?: number;
57
+ }
58
+ /**
59
+ * Options for awsLambda adapter.
60
+ */
61
+ interface AwsLambdaOptions extends AdapterOptions {
62
+ }
4
63
  /**
5
64
  * MIME types for cross-platform compatibility.
6
65
  */
@@ -41,15 +100,27 @@ interface ResourceConfig {
41
100
  displayModes: DisplayMode[];
42
101
  /**
43
102
  * HTML content for the resource.
44
- * Can be a string path (resolved relative to the server file's directory),
45
- * or a function that returns the HTML content.
103
+ *
104
+ * Accepts three formats:
105
+ * 1. **File path** (local development): `"ui/main.html"` - loaded from filesystem
106
+ * 2. **Raw HTML** (serverless-safe): `"<!DOCTYPE html>..."` - used directly
107
+ * 3. **Function** (lazy loading): `() => htmlContent` - called when needed
108
+ *
109
+ * The SDK auto-detects HTML content (starts with `<`) vs file paths.
110
+ * For serverless (Vercel, Lambda), use raw HTML or a function.
46
111
  *
47
112
  * @example
48
- * // Simple path (recommended)
113
+ * // Local development - file path
49
114
  * html: "ui/main.html"
50
115
  *
51
- * // Function for dynamic content
52
- * html: () => loadHtml("./ui/main.html")
116
+ * @example
117
+ * // Serverless - bundled HTML (import at build time)
118
+ * import { BUNDLED_HTML } from "./ui-bundle.js";
119
+ * html: BUNDLED_HTML
120
+ *
121
+ * @example
122
+ * // Serverless - function (lazy)
123
+ * html: () => fs.readFileSync("./dist/ui/main.html", "utf-8")
53
124
  */
54
125
  html: string | (() => string | Promise<string>);
55
126
  /** Optional icon for pips */
@@ -77,16 +148,10 @@ interface ResourceConfig {
77
148
  websocket?: boolean;
78
149
  }
79
150
  /**
80
- * Resource cache configuration.
81
- * Controls caching behavior for UI resource HTML content.
151
+ * Internal resource definition.
82
152
  */
83
- interface ResourceCacheConfig {
84
- /** Maximum number of cached entries (default: 100) */
85
- maxSize?: number;
86
- /** Time-to-live in milliseconds. 0 = no expiry (default: 0) */
87
- ttlMs?: number;
88
- /** Whether caching is enabled (default: true) */
89
- enabled?: boolean;
153
+ interface ResourceDefinition {
154
+ config: ResourceConfig;
90
155
  }
91
156
  /**
92
157
  * Tool configuration.
@@ -139,6 +204,30 @@ interface ToolContext {
139
204
  * Automatically attached to tool result for UI routing.
140
205
  */
141
206
  instanceId: string;
207
+ /**
208
+ * Creature App Token for identity verification.
209
+ * ONLY present when:
210
+ * 1. App opted into Creature-managed auth (`auth: { creatureManaged: true }`)
211
+ * 2. Tool call originated from Creature host
212
+ *
213
+ * Use `verifyCreatureToken(context.creatureToken)` to verify and extract
214
+ * user identity for multi-tenant data access.
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * app.tool("save_note", { ... }, async ({ content }, context) => {
219
+ * if (!context.creatureToken) {
220
+ * return { error: "Authentication required" };
221
+ * }
222
+ * const identity = await verifyCreatureToken(context.creatureToken);
223
+ * await db.notes.insert({
224
+ * user_id: identity.user.id,
225
+ * content,
226
+ * });
227
+ * });
228
+ * ```
229
+ */
230
+ creatureToken?: string;
142
231
  /**
143
232
  * Get server-side state for this instance.
144
233
  * State is NOT sent to UI — use for PIDs, connections, handles.
@@ -174,6 +263,13 @@ interface ToolContext {
174
263
  */
175
264
  websocketUrl: string | undefined;
176
265
  }
266
+ /**
267
+ * Internal tool definition with handler.
268
+ */
269
+ interface ToolDefinition<TInput = unknown> {
270
+ config: ToolConfig;
271
+ handler: ToolHandler<TInput>;
272
+ }
177
273
  /**
178
274
  * Context passed to onInstanceDestroy callback.
179
275
  */
@@ -199,6 +295,25 @@ interface TransportSessionInfo {
199
295
  /** The transport type for this session */
200
296
  transport: TransportType;
201
297
  }
298
+ /**
299
+ * Creature-managed authentication configuration.
300
+ * When enabled, Creature provides user identity and tokens to your app.
301
+ */
302
+ interface CreatureAuthConfig {
303
+ /**
304
+ * Enable Creature-managed authentication.
305
+ * When true, your app receives user identity (id, email, name) and
306
+ * organization/project context via hostContext.creature.
307
+ *
308
+ * Apps can use this for:
309
+ * - Auto-registering users without login screens
310
+ * - Scoping data to users/orgs/projects
311
+ * - Backend API calls with verified identity
312
+ *
313
+ * @default false
314
+ */
315
+ creatureManaged?: boolean;
316
+ }
202
317
  /**
203
318
  * App configuration.
204
319
  */
@@ -207,6 +322,14 @@ interface AppConfig {
207
322
  name: string;
208
323
  /** App version */
209
324
  version: string;
325
+ /**
326
+ * Authentication configuration.
327
+ * Enable Creature-managed auth to receive user identity automatically.
328
+ *
329
+ * @example
330
+ * auth: { creatureManaged: true }
331
+ */
332
+ auth?: CreatureAuthConfig;
210
333
  /**
211
334
  * High-level instructions for using this MCP.
212
335
  * Sent to the model during initialization to provide context about
@@ -259,11 +382,6 @@ interface AppConfig {
259
382
  * Controls how long to wait for in-flight requests to complete.
260
383
  */
261
384
  keepAliveTimeout?: number;
262
- /**
263
- * Configuration for resource content caching.
264
- * Resources are cached to avoid repeated file reads.
265
- */
266
- resourceCache?: ResourceCacheConfig;
267
385
  }
268
386
  /**
269
387
  * WebSocket connection for an instance.
@@ -302,8 +420,6 @@ declare class App {
302
420
  private callerDir;
303
421
  private shutdownRegistered;
304
422
  private isShuttingDown;
305
- private resourceCache;
306
- private resourceCacheConfig;
307
423
  /** Server-side instance state, keyed by instanceId. */
308
424
  private instanceState;
309
425
  /** Callbacks to invoke when an instance is destroyed. */
@@ -380,27 +496,73 @@ declare class App {
380
496
  */
381
497
  getTransportSessionCount(): number;
382
498
  /**
383
- * Close a specific transport session.
499
+ * Get the app configuration.
384
500
  */
385
- closeTransportSession(sessionId: string): boolean;
501
+ getConfig(): AppConfig;
386
502
  /**
387
- * Clear all cached resource content.
503
+ * Get all tool definitions.
388
504
  */
389
- clearResourceCache(): void;
505
+ getToolDefinitions(): Map<string, ToolDefinition>;
390
506
  /**
391
- * Clear a specific resource from the cache.
507
+ * Get all resource definitions.
392
508
  */
393
- clearResourceCacheEntry(uri: string): boolean;
509
+ getResourceDefinitions(): Map<string, ResourceDefinition>;
394
510
  /**
395
- * Get resource cache statistics.
511
+ * Create a Vercel MCP adapter configuration.
512
+ * For use with Vercel's `mcp-handler` package.
513
+ *
514
+ * Note: This returns a configuration callback. The implementation
515
+ * is inline to avoid circular dependencies.
396
516
  */
397
- getResourceCacheStats(): {
398
- size: number;
399
- maxSize: number;
400
- enabled: boolean;
401
- };
517
+ toVercelMcp(options?: VercelMcpOptions): (server: any, _context: any) => void;
518
+ /**
519
+ * Create an AWS Lambda handler.
520
+ *
521
+ * Note: This returns a Lambda handler function. The implementation
522
+ * is inline to avoid circular dependencies.
523
+ */
524
+ toAwsLambda(options?: AwsLambdaOptions): (event: any, _lambdaContext: any) => Promise<{
525
+ statusCode: number;
526
+ headers: {
527
+ "Access-Control-Allow-Origin": string;
528
+ "Access-Control-Allow-Methods": string;
529
+ "Access-Control-Allow-Headers": string;
530
+ "Content-Type": string;
531
+ };
532
+ body: string;
533
+ }>;
534
+ /**
535
+ * Close a specific transport session.
536
+ */
537
+ closeTransportSession(sessionId: string): boolean;
402
538
  private getPort;
403
539
  private getCallerDir;
540
+ /**
541
+ * Create a ToolContext for serverless environments.
542
+ * Shared between Vercel and Lambda adapters to avoid duplication.
543
+ */
544
+ /**
545
+ * In-memory state for serverless when no external adapter provided.
546
+ * Only useful for local development - won't persist across invocations in production.
547
+ */
548
+ private serverlessMemoryState;
549
+ private serverlessStateWarningLogged;
550
+ private serverlessRealtimeWarningLogged;
551
+ private createServerlessContext;
552
+ /**
553
+ * Format tool result for serverless response.
554
+ * Shared between Vercel and Lambda adapters.
555
+ */
556
+ private formatServerlessResult;
557
+ /**
558
+ * Create a Vercel MCP configuration callback.
559
+ * Returns a function compatible with Vercel's mcp-handler createMcpHandler.
560
+ */
561
+ private createServerlessConfig;
562
+ /**
563
+ * Create an AWS Lambda handler function.
564
+ */
565
+ private createLambdaHandler;
404
566
  private getHmrPort;
405
567
  private generateInstanceId;
406
568
  /**
@@ -421,8 +583,6 @@ declare class App {
421
583
  * Check if a field is required in a Zod schema.
422
584
  */
423
585
  private isFieldRequired;
424
- private getCachedResource;
425
- private setCachedResource;
426
586
  private createExpressApp;
427
587
  private handleMcpPost;
428
588
  private handleMcpGet;
@@ -459,10 +619,29 @@ declare function svgToDataUri(svg: string): string;
459
619
  */
460
620
  declare function loadHtml(filePath: string, basePath?: string): string;
461
621
  /**
462
- * Create an HTML loader function for a file path.
463
- * Useful for defining resources with lazy-loaded HTML.
622
+ * Check if a string is HTML content (vs a file path).
623
+ *
624
+ * HTML content detection:
625
+ * - Starts with `<` (with optional whitespace/BOM)
626
+ * - Starts with `<!DOCTYPE` (case-insensitive)
627
+ *
628
+ * This enables serverless deployments where HTML is bundled at build time
629
+ * and passed directly as a string, rather than loaded from the filesystem.
630
+ */
631
+ declare function isHtmlContent(str: string): boolean;
632
+ /**
633
+ * Create an HTML loader function for a file path or HTML content.
634
+ *
635
+ * For serverless environments (Vercel, Lambda), developers can:
636
+ * 1. Pass HTML content directly: `html: "<!DOCTYPE html>..."`
637
+ * 2. Use a function: `html: () => BUNDLED_HTML`
638
+ * 3. Import bundled HTML: `html: await import("./ui-bundle.js").then(m => m.HTML)`
639
+ *
640
+ * The SDK automatically detects whether the string is:
641
+ * - HTML content (starts with `<`) → returns as-is
642
+ * - File path → loads via filesystem (local dev only)
464
643
  */
465
- declare function htmlLoader(filePath: string, basePath?: string): () => string;
644
+ declare function htmlLoader(htmlOrPath: string, basePath?: string): () => string;
466
645
 
467
646
  /**
468
647
  * Middleware for adding cross-platform compatibility to the official MCP SDK.
@@ -520,4 +699,120 @@ declare function htmlLoader(filePath: string, basePath?: string): () => string;
520
699
  */
521
700
  declare function wrapServer<T extends McpServer>(server: T): T;
522
701
 
523
- export { App, type AppConfig, type DisplayMode, type IconConfig, type InstanceDestroyContext, MIME_TYPES, type ResourceCacheConfig, type ResourceConfig, type ToolConfig, type ToolContext, type ToolHandler, type ToolResult, type ToolVisibility, type TransportSessionInfo, type TransportType, type WebSocketConnection, createApp, htmlLoader, loadHtml, svgToDataUri, wrapServer };
702
+ /**
703
+ * User identity from a verified Creature token.
704
+ */
705
+ interface CreatureUser {
706
+ /** Unique, stable user identifier */
707
+ id: string;
708
+ /** User's email address */
709
+ email: string;
710
+ /** User's display name (may be undefined) */
711
+ name?: string;
712
+ }
713
+ /**
714
+ * Organization context from a verified Creature token.
715
+ */
716
+ interface CreatureOrganization {
717
+ /** Unique organization identifier */
718
+ id: string;
719
+ /** Organization display name */
720
+ name: string;
721
+ /** URL-safe organization slug */
722
+ slug: string;
723
+ }
724
+ /**
725
+ * Project context from a verified Creature token.
726
+ */
727
+ interface CreatureProject {
728
+ /** Unique project identifier */
729
+ id: string;
730
+ /** Project display name */
731
+ name: string;
732
+ }
733
+ /**
734
+ * Session context from a verified Creature token.
735
+ */
736
+ interface CreatureSession {
737
+ /** Unique session identifier */
738
+ id: string;
739
+ }
740
+ /**
741
+ * Verified identity claims from a Creature App Token.
742
+ * Returned by verifyCreatureToken() on successful verification.
743
+ */
744
+ interface CreatureIdentity {
745
+ /** User identity (always present for valid tokens) */
746
+ user: CreatureUser;
747
+ /** Organization context (present if user was in an org context) */
748
+ organization?: CreatureOrganization;
749
+ /** Project context (present if user was in a project context) */
750
+ project?: CreatureProject;
751
+ /** Session context */
752
+ session?: CreatureSession;
753
+ /** Token expiration time (ISO 8601 string) */
754
+ expiresAt: string;
755
+ }
756
+ /**
757
+ * Error thrown when token verification fails.
758
+ */
759
+ declare class CreatureTokenError extends Error {
760
+ /** Error code from the verification API */
761
+ code: string;
762
+ constructor({ code, message }: {
763
+ code: string;
764
+ message: string;
765
+ });
766
+ }
767
+ /**
768
+ * Configuration for the Creature token verification API.
769
+ */
770
+ interface VerifyConfig {
771
+ /**
772
+ * Base URL for the Creature API.
773
+ * Defaults to https://api.creature.run
774
+ * Can be overridden for testing or custom deployments.
775
+ */
776
+ apiUrl?: string;
777
+ }
778
+ /**
779
+ * Verifies a Creature App Token and returns the identity claims.
780
+ *
781
+ * Use this in your backend API to authenticate requests from your MCP App UI.
782
+ * The token is provided to your UI via `hostContext.creature.token` when you
783
+ * opt into Creature-managed auth.
784
+ *
785
+ * @param tokenOrHeader - The App Token (raw) or Authorization header ("Bearer <token>")
786
+ * @param config - Optional configuration (e.g., custom API URL)
787
+ * @returns The verified identity claims
788
+ * @throws {CreatureTokenError} If verification fails (invalid, expired, or malformed token)
789
+ *
790
+ * @example
791
+ * ```typescript
792
+ * import { verifyCreatureToken } from "@creature-ai/sdk/server";
793
+ *
794
+ * // In your Express/Hono/etc. API handler:
795
+ * app.post("/api/notes", async (req, res) => {
796
+ * try {
797
+ * const identity = await verifyCreatureToken(req.headers.authorization);
798
+ *
799
+ * // Use identity to scope data access
800
+ * const notes = await db.notes.find({
801
+ * user_id: identity.user.id,
802
+ * org_id: identity.organization?.id,
803
+ * });
804
+ *
805
+ * res.json({ notes });
806
+ * } catch (err) {
807
+ * if (err instanceof CreatureTokenError) {
808
+ * res.status(401).json({ error: err.code, message: err.message });
809
+ * } else {
810
+ * res.status(500).json({ error: "internal_error" });
811
+ * }
812
+ * }
813
+ * });
814
+ * ```
815
+ */
816
+ declare const verifyCreatureToken: (tokenOrHeader: string | undefined | null, config?: VerifyConfig) => Promise<CreatureIdentity>;
817
+
818
+ export { type AdapterOptions, App, type AppConfig, type AwsLambdaOptions, type CreatureIdentity, type CreatureOrganization, type CreatureProject, type CreatureSession, CreatureTokenError, type CreatureUser, type DisplayMode, type IconConfig, type InstanceDestroyContext, MIME_TYPES, type RealtimeAdapter, type ResourceConfig, type StateAdapter, type ToolConfig, type ToolContext, type ToolHandler, type ToolResult, type ToolVisibility, type TransportSessionInfo, type TransportType, type VercelMcpOptions, type WebSocketConnection, createApp, htmlLoader, isHtmlContent, loadHtml, svgToDataUri, verifyCreatureToken, wrapServer };