@creature-ai/sdk 0.1.1

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.
@@ -0,0 +1,543 @@
1
+ import { z } from 'zod';
2
+ import { Server } from 'http';
3
+ import { A as AppSessionOptions, W as WidgetState, a as AppSessionState, b as AppSessionListener } from '../types-DcoqlK46.js';
4
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+
6
+ /**
7
+ * MIME types for cross-platform compatibility.
8
+ */
9
+ declare const MIME_TYPES: {
10
+ readonly MCP_APPS: "text/html;profile=mcp-app";
11
+ readonly CHATGPT: "text/html+skybridge";
12
+ };
13
+ /**
14
+ * Display modes for UI resources.
15
+ */
16
+ type DisplayMode = "pip" | "inline" | "fullscreen";
17
+ /**
18
+ * Visibility options for tools.
19
+ * - "model": AI/Agent can call the tool
20
+ * - "app": UI can call the tool
21
+ */
22
+ type ToolVisibility = "model" | "app";
23
+ /**
24
+ * Icon configuration for UI resources.
25
+ */
26
+ interface IconConfig {
27
+ /** SVG string (must use currentColor for theming) */
28
+ svg: string;
29
+ /** Alt text for accessibility */
30
+ alt: string;
31
+ }
32
+ /**
33
+ * Resource configuration.
34
+ */
35
+ interface ResourceConfig {
36
+ /** Display name shown in UI */
37
+ name: string;
38
+ /** Resource URI (must start with ui://) */
39
+ uri: string;
40
+ /** Optional description */
41
+ description?: string;
42
+ /** Supported display modes */
43
+ displayModes: DisplayMode[];
44
+ /**
45
+ * HTML content for the resource.
46
+ * Can be a string path (resolved relative to the server file's directory),
47
+ * or a function that returns the HTML content.
48
+ *
49
+ * @example
50
+ * // Simple path (recommended)
51
+ * html: "ui/main.html"
52
+ *
53
+ * // Function for dynamic content
54
+ * html: () => loadHtml("./ui/main.html")
55
+ */
56
+ html: string | (() => string | Promise<string>);
57
+ /** Optional icon for pips */
58
+ icon?: IconConfig;
59
+ /** CSP configuration for external API access */
60
+ csp?: {
61
+ connectDomains?: string[];
62
+ resourceDomains?: string[];
63
+ };
64
+ }
65
+ /**
66
+ * Tool configuration.
67
+ */
68
+ interface ToolConfig<TInput extends z.ZodType = z.ZodType> {
69
+ /** Tool description (shown to AI) */
70
+ description: string;
71
+ /** Zod schema for input validation */
72
+ input?: TInput;
73
+ /** Resource URI to link this tool to a UI */
74
+ ui?: string;
75
+ /** Who can call this tool */
76
+ visibility?: ToolVisibility[];
77
+ /** Supported display modes for this tool */
78
+ displayModes?: DisplayMode[];
79
+ /** Preferred display mode */
80
+ defaultDisplayMode?: DisplayMode;
81
+ /**
82
+ * Whether this tool creates new sessions (multi-instance MCP).
83
+ * When true, the Host will always create a new pip instead of
84
+ * reusing an existing one for the same resourceUri.
85
+ * Use this for tools that spawn independent sessions (e.g., terminal_run).
86
+ */
87
+ multiSession?: boolean;
88
+ /** Loading message shown while tool is running (used by ChatGPT) */
89
+ loadingMessage?: string;
90
+ /** Completion message shown when tool finishes (used by ChatGPT) */
91
+ completedMessage?: string;
92
+ }
93
+ /**
94
+ * Tool result returned from handler.
95
+ */
96
+ interface ToolResult {
97
+ /** Structured data for UI rendering */
98
+ data?: Record<string, unknown>;
99
+ /** Text content for AI context */
100
+ text?: string;
101
+ /** Title for panel/widget */
102
+ title?: string;
103
+ /** Height hint for inline widgets (60-300px) */
104
+ inlineHeight?: number;
105
+ /** Whether this is an error result */
106
+ isError?: boolean;
107
+ }
108
+ /**
109
+ * Tool handler function type.
110
+ */
111
+ type ToolHandler<TInput> = (input: TInput, context: ToolContext) => ToolResult | Promise<ToolResult>;
112
+ /**
113
+ * Context passed to tool handlers.
114
+ * Reserved for future use.
115
+ */
116
+ interface ToolContext {
117
+ }
118
+ /**
119
+ * Resource cache configuration.
120
+ * Controls caching behavior for UI resource HTML content.
121
+ */
122
+ interface ResourceCacheConfig {
123
+ /** Maximum number of cached entries (default: 100) */
124
+ maxSize?: number;
125
+ /** Time-to-live in milliseconds. 0 = no expiry (default: 0) */
126
+ ttlMs?: number;
127
+ /** Whether caching is enabled (default: true) */
128
+ enabled?: boolean;
129
+ }
130
+ /**
131
+ * Supported MCP transport types.
132
+ * Currently only StreamableHTTP is supported by the SDK server.
133
+ * Stdio may be added in the future.
134
+ */
135
+ type TransportType = "streamable-http" | "stdio";
136
+ /**
137
+ * Information about a transport session.
138
+ * Provides details about an active MCP protocol connection.
139
+ */
140
+ interface TransportSessionInfo {
141
+ /** Unique session identifier */
142
+ id: string;
143
+ /** The transport type for this session */
144
+ transport: TransportType;
145
+ }
146
+ /**
147
+ * App configuration.
148
+ */
149
+ interface AppConfig {
150
+ /** App name (used in protocol handshake) */
151
+ name: string;
152
+ /** App version */
153
+ version: string;
154
+ /** Port for HTTP transport (default: 3000 or MCP_PORT env) */
155
+ port?: number;
156
+ /** Enable dev mode with HMR support (default: auto-detect from NODE_ENV) */
157
+ dev?: boolean;
158
+ /** HMR port override (default: auto-detect from Vite config or 5173) */
159
+ hmrPort?: number;
160
+ /**
161
+ * Called when a new transport session is created.
162
+ * Transport sessions are MCP protocol connections (not AppSessions).
163
+ */
164
+ onTransportSessionCreated?: (info: TransportSessionInfo) => void;
165
+ /**
166
+ * Called when a transport session is closed.
167
+ * Clients should re-initialize to continue.
168
+ */
169
+ onTransportSessionClosed?: (info: TransportSessionInfo) => void;
170
+ /**
171
+ * Called when a transport session error occurs.
172
+ * Useful for logging and monitoring connection health.
173
+ */
174
+ onTransportSessionError?: (info: TransportSessionInfo, error: Error) => void;
175
+ /**
176
+ * Called when a tool handler throws an error.
177
+ * The error is still returned to the client as an MCP error.
178
+ */
179
+ onToolError?: (toolName: string, error: Error, args: unknown) => void;
180
+ /**
181
+ * Called during graceful shutdown, before closing connections.
182
+ * Use this to clean up resources (e.g., close database connections).
183
+ */
184
+ onShutdown?: () => Promise<void> | void;
185
+ /**
186
+ * Timeout for graceful shutdown in milliseconds (default: 5000).
187
+ * After this timeout, remaining connections are force-closed.
188
+ */
189
+ gracefulShutdownTimeout?: number;
190
+ /**
191
+ * HTTP keep-alive timeout during shutdown in milliseconds (default: 5000).
192
+ * Controls how long to wait for in-flight requests to complete.
193
+ */
194
+ keepAliveTimeout?: number;
195
+ /**
196
+ * Configuration for resource content caching.
197
+ * Resources are cached to avoid repeated file reads.
198
+ */
199
+ resourceCache?: ResourceCacheConfig;
200
+ }
201
+
202
+ /**
203
+ * WebSocket channel support for real-time bidirectional communication.
204
+ *
205
+ * Channels provide a type-safe abstraction over WebSockets, allowing MCP Apps
206
+ * to stream data to connected UIs without polling.
207
+ */
208
+
209
+ /**
210
+ * Handler for incoming client messages.
211
+ */
212
+ type MessageHandler<T> = (message: T) => void;
213
+ /**
214
+ * Configuration for a channel definition.
215
+ */
216
+ interface ChannelConfig<_TServer = unknown, _TClient = unknown> {
217
+ /** Zod schema for server → client messages (optional, for documentation) */
218
+ server?: z.ZodType;
219
+ /** Zod schema for client → server messages (validated on receive) */
220
+ client?: z.ZodType;
221
+ }
222
+ /**
223
+ * A WebSocket channel instance for a specific AppSession.
224
+ *
225
+ * This provides real-time bidirectional communication between the server
226
+ * and all UI clients connected to a particular AppSession.
227
+ */
228
+ interface AppSessionChannel<TServer = unknown, TClient = unknown> {
229
+ /** The AppSession ID this channel belongs to */
230
+ appSessionId: string;
231
+ /** WebSocket URL for clients to connect */
232
+ url: string;
233
+ /** Send a message to all connected clients */
234
+ send: (message: TServer) => void;
235
+ /** Register a handler for incoming client messages */
236
+ onMessage: (handler: MessageHandler<TClient>) => void;
237
+ /** Register a handler called when a new client connects */
238
+ onConnect: (handler: () => void) => void;
239
+ /** Close the channel and disconnect all clients */
240
+ close: () => void;
241
+ /** Number of connected clients */
242
+ readonly clientCount: number;
243
+ }
244
+ /**
245
+ * Manages WebSocket connections and routes messages to AppSession channels.
246
+ */
247
+ declare class ChannelManager {
248
+ private wss;
249
+ private channels;
250
+ /**
251
+ * Attach the WebSocket server to an HTTP server.
252
+ */
253
+ attach(server: Server): void;
254
+ /**
255
+ * Create a new channel for an AppSession.
256
+ *
257
+ * @param channelName - The channel name (e.g., "updates", "terminal")
258
+ * @param appSessionId - The AppSession ID this channel belongs to
259
+ * @param config - Channel configuration
260
+ * @param port - Server port for WebSocket URL
261
+ * @returns An AppSessionChannel for bidirectional communication
262
+ */
263
+ createChannel<TServer, TClient>(channelName: string, appSessionId: string, config: ChannelConfig<TServer, TClient>, port: number): AppSessionChannel<TServer, TClient>;
264
+ /**
265
+ * Check if a channel exists for an AppSession.
266
+ */
267
+ hasChannel(channelName: string, appSessionId: string): boolean;
268
+ /**
269
+ * Check if any channels exist.
270
+ */
271
+ hasAnyChannels(): boolean;
272
+ /**
273
+ * Close all channels and shut down the WebSocket server.
274
+ */
275
+ closeAll(): void;
276
+ }
277
+ /**
278
+ * A channel definition that can create AppSession channel instances.
279
+ */
280
+ declare class ChannelDefinition<TServer = unknown, TClient = unknown> {
281
+ private manager;
282
+ private name;
283
+ private config;
284
+ private getPort;
285
+ constructor(manager: ChannelManager, name: string, config: ChannelConfig<TServer, TClient>, getPort: () => number);
286
+ /**
287
+ * Create a channel for a specific AppSession.
288
+ *
289
+ * @param appSessionId - The AppSession ID (e.g., from a terminal AppSession)
290
+ * @returns An AppSessionChannel for bidirectional communication
291
+ */
292
+ forAppSession(appSessionId: string): AppSessionChannel<TServer, TClient>;
293
+ /**
294
+ * Check if a channel exists for an AppSession.
295
+ */
296
+ hasChannel(appSessionId: string): boolean;
297
+ /**
298
+ * Get the channel name.
299
+ */
300
+ get channelName(): string;
301
+ }
302
+
303
+ type PartialState<TState extends AppSessionState> = {
304
+ [K in keyof TState]?: Partial<TState[K]> | TState[K];
305
+ };
306
+ interface ServerAppSessionOptions extends AppSessionOptions {
307
+ websockets?: boolean;
308
+ }
309
+ /**
310
+ * Server-side AppSession for managing application state.
311
+ *
312
+ * AppSessions are distinct from TransportSessions:
313
+ * - AppSession: Application-level state (internal, backend, ui)
314
+ * - TransportSession: MCP protocol connection (StreamableHTTP session)
315
+ */
316
+ declare class ServerAppSession<TInternal extends Record<string, unknown> = Record<string, unknown>, TBackend extends Record<string, unknown> = Record<string, unknown>, TUi extends WidgetState | null = WidgetState | null> {
317
+ readonly id: string;
318
+ private _state;
319
+ private listeners;
320
+ private _channel;
321
+ constructor(initialState?: Partial<AppSessionState<TInternal, TBackend, TUi>>, options?: {
322
+ id?: string;
323
+ });
324
+ private generateId;
325
+ get state(): AppSessionState<TInternal, TBackend, TUi>;
326
+ get internal(): TInternal;
327
+ get backend(): TBackend;
328
+ get ui(): TUi;
329
+ get channel(): AppSessionChannel<unknown, unknown> | null;
330
+ get channelUrl(): string | undefined;
331
+ setChannel(channel: AppSessionChannel<unknown, unknown>): void;
332
+ subscribe(listener: AppSessionListener<AppSessionState<TInternal, TBackend, TUi>>): () => void;
333
+ private notify;
334
+ setState(partial: PartialState<AppSessionState<TInternal, TBackend, TUi>>): void;
335
+ setInternal(internal: TInternal | Partial<TInternal>): void;
336
+ setBackend(backend: TBackend | Partial<TBackend>): void;
337
+ setUi(ui: TUi): void;
338
+ updateUi(partial: TUi extends null ? never : Partial<NonNullable<TUi>>): void;
339
+ injectAppSessionId<T extends Record<string, unknown>>(data: T): T & {
340
+ appSessionId: string;
341
+ };
342
+ close(): void;
343
+ }
344
+ /**
345
+ * Manages ServerAppSession instances.
346
+ *
347
+ * This manages AppSessions (application state), not TransportSessions
348
+ * (MCP protocol connections).
349
+ */
350
+ declare class AppSessionManager {
351
+ private appSessions;
352
+ private channelManager;
353
+ private getPort;
354
+ private appSessionChannelName;
355
+ constructor(config: {
356
+ channelManager?: ChannelManager;
357
+ getPort: () => number;
358
+ });
359
+ setChannelManager(channelManager: ChannelManager): void;
360
+ create<TInternal extends Record<string, unknown> = Record<string, unknown>, TBackend extends Record<string, unknown> = Record<string, unknown>, TUi extends WidgetState | null = WidgetState | null>(initialState?: Partial<AppSessionState<TInternal, TBackend, TUi>>, options?: ServerAppSessionOptions): ServerAppSession<TInternal, TBackend, TUi>;
361
+ get(id: string): ServerAppSession | undefined;
362
+ require(id: string): ServerAppSession;
363
+ has(id: string): boolean;
364
+ delete(id: string): boolean;
365
+ closeAll(): void;
366
+ get size(): number;
367
+ }
368
+
369
+ declare class App {
370
+ private config;
371
+ private tools;
372
+ private resources;
373
+ private transports;
374
+ private channelManager;
375
+ private channels;
376
+ private appSessionManager;
377
+ private httpServer;
378
+ private isDev;
379
+ private hmrPort;
380
+ private hmrConfigChecked;
381
+ private callerDir;
382
+ private shutdownRegistered;
383
+ private isShuttingDown;
384
+ private resourceCache;
385
+ private resourceCacheConfig;
386
+ constructor(config: AppConfig, callerDir?: string);
387
+ /**
388
+ * Get list of active transport sessions.
389
+ * Transport sessions are MCP protocol connections (not AppSessions).
390
+ * Useful for monitoring and debugging connection state.
391
+ */
392
+ getTransportSessions(): TransportSessionInfo[];
393
+ /**
394
+ * Get the count of active transport sessions.
395
+ */
396
+ getTransportSessionCount(): number;
397
+ /**
398
+ * Close a specific transport session.
399
+ *
400
+ * @param sessionId - The transport session ID to close
401
+ * @returns true if the session was found and closed
402
+ */
403
+ closeTransportSession(sessionId: string): boolean;
404
+ /**
405
+ * Clear all cached resource content.
406
+ * Use after updating UI files during development.
407
+ */
408
+ clearResourceCache(): void;
409
+ /**
410
+ * Clear a specific resource from the cache.
411
+ *
412
+ * @param uri - The resource URI to clear
413
+ * @returns true if the resource was in the cache and removed
414
+ */
415
+ clearResourceCacheEntry(uri: string): boolean;
416
+ /**
417
+ * Get resource cache statistics.
418
+ * Useful for monitoring cache performance.
419
+ */
420
+ getResourceCacheStats(): {
421
+ size: number;
422
+ maxSize: number;
423
+ enabled: boolean;
424
+ };
425
+ /**
426
+ * Get a cached resource if valid, or null if not cached/expired.
427
+ */
428
+ private getCachedResource;
429
+ /**
430
+ * Store a resource in the cache with LRU eviction.
431
+ */
432
+ private setCachedResource;
433
+ /**
434
+ * Get the HMR port, reading from hmr.json lazily if not already set.
435
+ * This handles the timing issue where Vite may not have written hmr.json
436
+ * when the server first starts.
437
+ */
438
+ private getHmrPort;
439
+ private getCallerDir;
440
+ /**
441
+ * Define a UI resource.
442
+ */
443
+ resource(config: ResourceConfig): this;
444
+ tool<TInput extends z.ZodType>(name: string, config: ToolConfig<TInput>, handler: ToolHandler<z.infer<TInput>>): this;
445
+ channel<TServer = unknown, TClient = unknown>(name: string, config?: ChannelConfig<TServer, TClient>): ChannelDefinition<TServer, TClient>;
446
+ private getPort;
447
+ session<TInternal extends Record<string, unknown> = Record<string, unknown>, TBackend extends Record<string, unknown> = Record<string, unknown>, TUi extends WidgetState | null = WidgetState | null>(initialState?: Partial<AppSessionState<TInternal, TBackend, TUi>>, options?: ServerAppSessionOptions): ServerAppSession<TInternal, TBackend, TUi>;
448
+ getSession(id: string): ServerAppSession | undefined;
449
+ requireSession(id: string): ServerAppSession;
450
+ hasSession(id: string): boolean;
451
+ closeSession(id: string): boolean;
452
+ /**
453
+ * Create an MCP server instance with all registered tools and resources.
454
+ */
455
+ private createMcpServer;
456
+ /**
457
+ * Format a tool result into MCP format.
458
+ */
459
+ private formatToolResult;
460
+ start(): Promise<void>;
461
+ /**
462
+ * Stop the MCP server gracefully.
463
+ *
464
+ * Calls the onShutdown callback, closes all sessions and channels,
465
+ * then waits for active transports to close (with timeout).
466
+ */
467
+ stop(): Promise<void>;
468
+ }
469
+ /**
470
+ * Create a new MCP App.
471
+ */
472
+ declare function createApp(config: AppConfig): App;
473
+
474
+ declare function svgToDataUri(svg: string): string;
475
+ /**
476
+ * Load HTML from a file path.
477
+ * Automatically resolves paths like "ui/main.html" to "dist/ui/main.html"
478
+ * regardless of whether running from src/ or dist/.
479
+ */
480
+ declare function loadHtml(filePath: string, basePath?: string): string;
481
+ /**
482
+ * Create an HTML loader function for a file path.
483
+ * Useful for defining resources with lazy-loaded HTML.
484
+ */
485
+ declare function htmlLoader(filePath: string, basePath?: string): () => string;
486
+
487
+ /**
488
+ * Middleware for adding cross-platform compatibility to the official MCP SDK.
489
+ *
490
+ * Wrap your McpServer to automatically output dual formats for both
491
+ * MCP Apps (Creature) and ChatGPT Apps SDK.
492
+ *
493
+ * @example
494
+ * ```ts
495
+ * import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
496
+ * import { wrapServer } from "@creature-ai/sdk/server";
497
+ *
498
+ * const server = wrapServer(new McpServer({ name: "my-app", version: "1.0.0" }));
499
+ *
500
+ * // Use the standard MCP SDK API - dual format is automatic
501
+ * server.registerResource("Panel", "ui://app/panel", { ... }, handler);
502
+ * server.registerTool("search", { ... }, handler);
503
+ * ```
504
+ */
505
+
506
+ /**
507
+ * Wrap an McpServer to automatically add dual-format support.
508
+ *
509
+ * This intercepts `registerResource` and `registerTool` calls to automatically
510
+ * include both MCP Apps and ChatGPT metadata/MIME types.
511
+ *
512
+ * @param server - An McpServer instance from @modelcontextprotocol/sdk
513
+ * @returns The same server with intercepted registration methods
514
+ *
515
+ * @example
516
+ * ```ts
517
+ * import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
518
+ * import { wrapServer } from "@creature-ai/sdk/server";
519
+ *
520
+ * const server = wrapServer(new McpServer({ name: "my-app", version: "1.0.0" }));
521
+ *
522
+ * // Register resources normally - dual MIME types added automatically
523
+ * server.registerResource("Panel", "ui://app/panel", {
524
+ * mimeType: "text/html",
525
+ * description: "My panel",
526
+ * }, async () => ({
527
+ * contents: [{ uri: "ui://app/panel", mimeType: "text/html", text: html }],
528
+ * }));
529
+ *
530
+ * // Register tools normally - dual metadata added automatically
531
+ * server.registerTool("search", {
532
+ * description: "Search",
533
+ * inputSchema: z.object({ query: z.string() }),
534
+ * _meta: {
535
+ * ui: { resourceUri: "ui://app/panel", displayModes: ["pip"] },
536
+ * loadingMessage: "Searching...", // Will become openai/toolInvocation/invoking
537
+ * },
538
+ * }, handler);
539
+ * ```
540
+ */
541
+ declare function wrapServer<T extends McpServer>(server: T): T;
542
+
543
+ export { App, type AppConfig, type AppSessionChannel, AppSessionListener, AppSessionManager, AppSessionOptions, AppSessionState, type ChannelConfig, ChannelDefinition, type DisplayMode, type IconConfig, MIME_TYPES, type ResourceCacheConfig, type ResourceConfig, ServerAppSession, type ServerAppSessionOptions, type ToolConfig, type ToolContext, type ToolHandler, type ToolResult, type ToolVisibility, type TransportSessionInfo, type TransportType, createApp, htmlLoader, loadHtml, svgToDataUri, wrapServer };