@pancake-apps/server 0.0.0-snapshot-20260125200133

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,518 @@
1
+ import { createAdapter } from './chunk-CVG3DAN6.js';
2
+ import express, { Router } from 'express';
3
+ import * as fs from 'fs/promises';
4
+ import * as path from 'path';
5
+
6
+ // src/utils/detect-protocol.ts
7
+ function detectProtocol(req, defaultProtocol = "mcp") {
8
+ const headers = req.headers;
9
+ const accept = getHeader(headers, "accept");
10
+ if (accept && accept.includes("text/html+skybridge")) {
11
+ return "openai";
12
+ }
13
+ const openaiHeader = getHeader(headers, "x-openai-client");
14
+ if (openaiHeader) {
15
+ return "openai";
16
+ }
17
+ const userAgent = getHeader(headers, "user-agent");
18
+ if (userAgent && (userAgent.includes("ChatGPT") || userAgent.includes("OpenAI"))) {
19
+ return "openai";
20
+ }
21
+ return defaultProtocol;
22
+ }
23
+ function getHeader(headers, name) {
24
+ const value = headers[name] || headers[name.toLowerCase()];
25
+ if (Array.isArray(value)) {
26
+ return value[0];
27
+ }
28
+ return value;
29
+ }
30
+
31
+ // src/server/mcp.ts
32
+ function createMcpHandler(config) {
33
+ return async (req, res) => {
34
+ const request = req.body;
35
+ if (request.jsonrpc !== "2.0") {
36
+ res.status(400).json({
37
+ jsonrpc: "2.0",
38
+ id: request.id ?? null,
39
+ error: { code: -32600, message: "Invalid Request: Not JSON-RPC 2.0" }
40
+ });
41
+ return;
42
+ }
43
+ try {
44
+ const protocol = detectProtocol(req, config.defaultProtocol);
45
+ const adapter = createAdapter(protocol);
46
+ const result = await handleMethod(config, request.method, request.params ?? {}, adapter);
47
+ const response = {
48
+ jsonrpc: "2.0",
49
+ id: request.id,
50
+ result
51
+ };
52
+ res.json(response);
53
+ } catch (error) {
54
+ const response = {
55
+ jsonrpc: "2.0",
56
+ id: request.id,
57
+ error: {
58
+ code: -32603,
59
+ message: error instanceof Error ? error.message : "Internal error",
60
+ data: error instanceof Error ? { stack: error.stack } : void 0
61
+ }
62
+ };
63
+ res.json(response);
64
+ }
65
+ };
66
+ }
67
+ async function handleMethod(config, method, params, adapter) {
68
+ switch (method) {
69
+ case "initialize":
70
+ return {
71
+ protocolVersion: "2025-11-21",
72
+ capabilities: {
73
+ tools: {},
74
+ resources: {}
75
+ },
76
+ serverInfo: {
77
+ name: config.name,
78
+ version: config.version
79
+ }
80
+ };
81
+ case "tools/list":
82
+ return {
83
+ tools: Array.from(config.tools.values()).filter((tool) => tool.visibility !== "app").map((tool) => {
84
+ const uiUri = tool.ui ? getUIResourceUri(tool.name, tool.category) : void 0;
85
+ const meta = adapter.buildToolMeta(tool, uiUri);
86
+ return {
87
+ name: tool.name,
88
+ description: tool.description,
89
+ inputSchema: tool.inputSchema,
90
+ annotations: meta.annotations,
91
+ _meta: tool.ui ? meta._meta : void 0
92
+ };
93
+ })
94
+ };
95
+ case "tools/call": {
96
+ const toolName = params["name"];
97
+ const args = params["arguments"] ?? {};
98
+ const result = await config.executeTool(toolName, args);
99
+ return {
100
+ content: [
101
+ {
102
+ type: "text",
103
+ text: JSON.stringify(result)
104
+ }
105
+ ],
106
+ structuredContent: result
107
+ };
108
+ }
109
+ case "resources/list":
110
+ return {
111
+ resources: Array.from(config.uiResources.values()).map((resource) => {
112
+ const meta = adapter.buildUIResourceMeta(resource.definition);
113
+ return {
114
+ uri: resource.uri,
115
+ name: resource.name,
116
+ mimeType: meta.mimeType,
117
+ _meta: meta._meta
118
+ };
119
+ })
120
+ };
121
+ case "resources/read": {
122
+ const uri = params["uri"];
123
+ const resource = config.uiResources.get(uri);
124
+ if (!resource) {
125
+ throw new Error(`Resource not found: ${uri}`);
126
+ }
127
+ let htmlContent;
128
+ const htmlPath = resource.definition.html;
129
+ if (htmlPath.trim().startsWith("<")) {
130
+ htmlContent = htmlPath;
131
+ } else {
132
+ const fsPromises = await import('fs/promises');
133
+ const pathModule = await import('path');
134
+ const fullPath = pathModule.resolve(process.cwd(), htmlPath);
135
+ htmlContent = await fsPromises.readFile(fullPath, "utf-8");
136
+ }
137
+ return {
138
+ contents: [
139
+ {
140
+ uri,
141
+ mimeType: "text/html",
142
+ text: htmlContent
143
+ }
144
+ ]
145
+ };
146
+ }
147
+ case "notifications/initialized":
148
+ case "ping":
149
+ return {};
150
+ default:
151
+ throw new Error(`Unknown method: ${method}`);
152
+ }
153
+ }
154
+ function getUIResourceUri(toolName, category) {
155
+ const name = toolName.replace(/^(view|action|data|tool):/, "");
156
+ return `pancake://ui/${category}/${name}`;
157
+ }
158
+
159
+ // src/server/dev-proxy.ts
160
+ function isDevelopment() {
161
+ return process.env["NODE_ENV"] !== "production";
162
+ }
163
+ function createViteProxy(config) {
164
+ const vitePort = config.vitePort ?? 5173;
165
+ const viteUrl = `http://localhost:${vitePort}`;
166
+ const vitePaths = [
167
+ "/@vite/",
168
+ "/@react-refresh",
169
+ "/assets/",
170
+ "/src/",
171
+ "/__vite_ping",
172
+ "/@fs/",
173
+ "/node_modules/.vite/",
174
+ "/node_modules/.pnpm/"
175
+ // pnpm store - Vite client imports from here
176
+ ];
177
+ return async (req, res, next) => {
178
+ const shouldProxy = vitePaths.some((p) => req.path.startsWith(p));
179
+ if (!shouldProxy) {
180
+ next();
181
+ return;
182
+ }
183
+ try {
184
+ const targetUrl = `${viteUrl}${req.url}`;
185
+ const response = await fetch(targetUrl, {
186
+ method: req.method,
187
+ headers: {
188
+ ...Object.fromEntries(
189
+ Object.entries(req.headers).filter(
190
+ ([key]) => !["host", "connection"].includes(key.toLowerCase())
191
+ )
192
+ )
193
+ }
194
+ });
195
+ response.headers.forEach((value, key) => {
196
+ if (key.toLowerCase() !== "transfer-encoding") {
197
+ res.setHeader(key, value);
198
+ }
199
+ });
200
+ res.status(response.status);
201
+ const body = await response.arrayBuffer();
202
+ res.end(Buffer.from(body));
203
+ } catch (error) {
204
+ console.error(`[Pancake] Vite proxy failed for ${req.path}: ${error.message}`);
205
+ next();
206
+ }
207
+ };
208
+ }
209
+ function generateHMRTemplate(viewName) {
210
+ return `<!DOCTYPE html>
211
+ <html lang="en">
212
+ <head>
213
+ <meta charset="UTF-8" />
214
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
215
+ <title>${viewName}</title>
216
+ <!-- All Vite resources go through the proxy using relative URLs -->
217
+ <script type="module">
218
+ import { injectIntoGlobalHook } from "/@react-refresh";
219
+ injectIntoGlobalHook(window);
220
+ window.$RefreshReg$ = () => {};
221
+ window.$RefreshSig$ = () => (type) => type;
222
+ window.__vite_plugin_react_preamble_installed__ = true;
223
+ </script>
224
+ <script type="module" src="/@vite/client"></script>
225
+ </head>
226
+ <body>
227
+ <div id="root"></div>
228
+ <script type="module">
229
+ // Initialize Pancake client before loading the view
230
+ import('/src/pancake-init.ts')
231
+ .then(async (mod) => {
232
+ if (mod.init) await mod.init();
233
+ })
234
+ .catch(() => {
235
+ // pancake-init.ts might not exist, that's ok
236
+ })
237
+ .finally(() => {
238
+ // Try each path and log which one fails and why
239
+ const paths = [
240
+ '/src/views/${viewName}/index.tsx',
241
+ '/src/views/${viewName}.tsx',
242
+ '/src/views/${viewName}/index.ts',
243
+ '/src/views/${viewName}.ts'
244
+ ];
245
+
246
+ async function tryImport(index) {
247
+ if (index >= paths.length) {
248
+ document.body.innerHTML = '<pre style="color: red;">Failed to load view: No valid module found</pre>';
249
+ return;
250
+ }
251
+ const path = paths[index];
252
+ try {
253
+ console.log('[Pancake] Trying to import:', path);
254
+ await import(path);
255
+ console.log('[Pancake] Successfully imported:', path);
256
+ } catch (err) {
257
+ console.error('[Pancake] Failed to import ' + path + ':', err);
258
+ // If this was the first path (the expected one), show the error
259
+ if (index === 0) {
260
+ document.body.innerHTML = '<pre style="color: red;">Failed to load view ' + path + ':\\n\\n' + err.message + '\\n\\n' + (err.stack || '') + '</pre>';
261
+ } else {
262
+ tryImport(index + 1);
263
+ }
264
+ }
265
+ }
266
+
267
+ tryImport(0);
268
+ });
269
+ </script>
270
+ </body>
271
+ </html>`;
272
+ }
273
+
274
+ // src/server/routes.ts
275
+ function createUIRoutes(uiResources, devServerConfig) {
276
+ const router = Router();
277
+ router.get("/:category/:name", async (req, res) => {
278
+ const { category, name } = req.params;
279
+ const uri = `pancake://ui/${category}/${name}`;
280
+ const resource = uiResources.get(uri);
281
+ if (!resource) {
282
+ res.status(404).json({ error: `UI not found: ${category}/${name}` });
283
+ return;
284
+ }
285
+ try {
286
+ const html = await getUIHtml(resource.definition, name, devServerConfig);
287
+ res.type("html").send(html);
288
+ } catch (error) {
289
+ res.status(500).json({
290
+ error: "Failed to load UI",
291
+ details: error instanceof Error ? error.message : String(error)
292
+ });
293
+ }
294
+ });
295
+ router.get("/:name", async (req, res) => {
296
+ const { name } = req.params;
297
+ for (const category of ["view", "action", "tool"]) {
298
+ const uri = `pancake://ui/${category}/${name}`;
299
+ const resource = uiResources.get(uri);
300
+ if (resource) {
301
+ try {
302
+ const html = await getUIHtml(resource.definition, name, devServerConfig);
303
+ res.type("html").send(html);
304
+ return;
305
+ } catch (error) {
306
+ res.status(500).json({
307
+ error: "Failed to load UI",
308
+ details: error instanceof Error ? error.message : String(error)
309
+ });
310
+ return;
311
+ }
312
+ }
313
+ }
314
+ res.status(404).json({ error: `UI not found: ${name}` });
315
+ });
316
+ return router;
317
+ }
318
+ async function getUIHtml(ui, viewName, devServerConfig) {
319
+ if (isDevelopment() && devServerConfig) {
320
+ return generateHMRTemplate(viewName);
321
+ }
322
+ if (ui.html.trim().startsWith("<")) {
323
+ return ui.html;
324
+ }
325
+ const htmlPath = path.resolve(process.cwd(), ui.html);
326
+ const content = await fs.readFile(htmlPath, "utf-8");
327
+ return content;
328
+ }
329
+
330
+ // src/server/index.ts
331
+ function generateRequestId() {
332
+ return `req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
333
+ }
334
+ async function createServer(config) {
335
+ const app = express();
336
+ app.use(express.json());
337
+ if (config.config?.cors) {
338
+ const corsConfig = config.config.cors;
339
+ app.use((req, res, next) => {
340
+ const origin = corsConfig.origin;
341
+ if (origin === true) {
342
+ res.setHeader("Access-Control-Allow-Origin", "*");
343
+ } else if (typeof origin === "string") {
344
+ res.setHeader("Access-Control-Allow-Origin", origin);
345
+ } else if (Array.isArray(origin)) {
346
+ const requestOrigin = req.headers.origin;
347
+ if (requestOrigin && origin.includes(requestOrigin)) {
348
+ res.setHeader("Access-Control-Allow-Origin", requestOrigin);
349
+ }
350
+ }
351
+ if (corsConfig.credentials) {
352
+ res.setHeader("Access-Control-Allow-Credentials", "true");
353
+ }
354
+ if (corsConfig.methods) {
355
+ res.setHeader("Access-Control-Allow-Methods", corsConfig.methods.join(", "));
356
+ }
357
+ if (corsConfig.allowedHeaders) {
358
+ res.setHeader("Access-Control-Allow-Headers", corsConfig.allowedHeaders.join(", "));
359
+ }
360
+ if (req.method === "OPTIONS") {
361
+ res.status(204).end();
362
+ return;
363
+ }
364
+ next();
365
+ });
366
+ }
367
+ if (isDevelopment() && config.config?.devServer) {
368
+ app.use(createViteProxy(config.config.devServer));
369
+ }
370
+ const uiRoutes = createUIRoutes(config.uiResources, config.config?.devServer);
371
+ app.use("/ui", uiRoutes);
372
+ const executeTool = async (toolName, input, metadata = {}) => {
373
+ const tool = config.tools.get(toolName);
374
+ if (!tool) {
375
+ throw new Error(`Unknown tool: ${toolName}`);
376
+ }
377
+ const ctx = {
378
+ requestId: generateRequestId(),
379
+ toolName,
380
+ metadata
381
+ };
382
+ const startTime = Date.now();
383
+ try {
384
+ const result = await config.middlewareChain.execute(
385
+ { toolName, input, metadata },
386
+ () => tool.handler(input, ctx)
387
+ );
388
+ config.events.emit("tool:success", {
389
+ toolName,
390
+ result,
391
+ durationMs: Date.now() - startTime
392
+ });
393
+ return result;
394
+ } catch (error) {
395
+ config.events.emit("tool:error", {
396
+ toolName,
397
+ error,
398
+ durationMs: Date.now() - startTime
399
+ });
400
+ throw error;
401
+ }
402
+ };
403
+ const mcpRoute = config.config?.serverRoute ?? "/mcp";
404
+ const mcpHandler = createMcpHandler({
405
+ name: config.name,
406
+ version: config.version,
407
+ tools: config.tools,
408
+ uiResources: config.uiResources,
409
+ executeTool,
410
+ defaultProtocol: config.config?.protocol
411
+ });
412
+ app.post(mcpRoute, mcpHandler);
413
+ app.get("/health", (_req, res) => {
414
+ res.json({ status: "ok", name: config.name, version: config.version });
415
+ });
416
+ if (config.transport === "stdio") {
417
+ const { startStdioServer } = await import('./stdio-ZTUR5PV4.js');
418
+ return startStdioServer({
419
+ name: config.name,
420
+ version: config.version,
421
+ tools: config.tools,
422
+ uiResources: config.uiResources,
423
+ executeTool,
424
+ protocol: config.config?.protocol
425
+ });
426
+ }
427
+ return new Promise((resolve2) => {
428
+ const httpServer = app.listen(config.port, config.host, async () => {
429
+ const baseUrl = `http://localhost:${config.port}`;
430
+ const viewNames = config.viewNames ?? [];
431
+ const actionNames = config.actionNames ?? [];
432
+ const dataNames = config.dataNames ?? [];
433
+ let tunnel = null;
434
+ if (config.config?.tunnel) {
435
+ const provider = config.config.tunnel.provider;
436
+ const { isTunnelProviderAvailable, createTunnel } = await import('./tunnel-AGGQW6IC.js');
437
+ const isAvailable = await isTunnelProviderAvailable(provider);
438
+ if (!isAvailable) {
439
+ console.log("");
440
+ console.log(` \x1B[33m\u26A0\x1B[0m Tunnel provider "${provider}" is not installed`);
441
+ if (provider === "cloudflare") {
442
+ console.log(` Install with: \x1B[1mbrew install cloudflared\x1B[0m`);
443
+ } else {
444
+ console.log(` Install with: \x1B[1mbrew install ngrok\x1B[0m`);
445
+ }
446
+ console.log(` Continuing without tunnel...`);
447
+ console.log("");
448
+ } else {
449
+ console.log(` \x1B[36m\u2192\x1B[0m Starting ${provider} tunnel...`);
450
+ try {
451
+ const tunnelConfig = {
452
+ provider,
453
+ port: config.port
454
+ };
455
+ if (config.config.tunnel.authtoken) {
456
+ tunnelConfig.authtoken = config.config.tunnel.authtoken;
457
+ }
458
+ tunnel = await createTunnel(tunnelConfig);
459
+ console.log(` \x1B[32m\u2713\x1B[0m Tunnel started: \x1B[1m${tunnel.url}\x1B[0m`);
460
+ } catch (error) {
461
+ console.log(` \x1B[31m\u2717\x1B[0m Tunnel failed: ${error.message}`);
462
+ }
463
+ }
464
+ }
465
+ if (config.config?.debug) {
466
+ console.log("");
467
+ console.log(` \x1B[1m\x1B[32m\u2713\x1B[0m \x1B[1m${config.name}\x1B[0m is running`);
468
+ console.log("");
469
+ console.log(` \x1B[2m\u279C\x1B[0m \x1B[1mServer:\x1B[0m ${baseUrl}`);
470
+ console.log(` \x1B[2m\u279C\x1B[0m \x1B[1mMCP:\x1B[0m ${baseUrl}${mcpRoute}`);
471
+ if (viewNames.length > 0) {
472
+ console.log(` \x1B[2m\u279C\x1B[0m \x1B[1mViews:\x1B[0m ${viewNames.map((v) => `${baseUrl}/ui/${v}`).join("\n ")}`);
473
+ }
474
+ if (tunnel) {
475
+ const providerName = config.config.tunnel?.provider === "cloudflare" ? "Cloudflare" : "ngrok";
476
+ console.log("");
477
+ console.log(` \x1B[1m\x1B[35m\u26A1\x1B[0m \x1B[1mTunnel (${providerName}):\x1B[0m`);
478
+ console.log(` \x1B[2m\u279C\x1B[0m \x1B[1mPublic:\x1B[0m ${tunnel.url}`);
479
+ console.log(` \x1B[2m\u279C\x1B[0m \x1B[1mMCP:\x1B[0m ${tunnel.url}${mcpRoute}`);
480
+ console.log("");
481
+ console.log(` \x1B[33m\u26A0\x1B[0m \x1B[2mTunnel URL is ephemeral and will change on restart\x1B[0m`);
482
+ }
483
+ console.log("");
484
+ console.log(` \x1B[2mRegistered:\x1B[0m`);
485
+ console.log(` ${viewNames.length} view${viewNames.length !== 1 ? "s" : ""}${viewNames.length > 0 ? ` (${viewNames.join(", ")})` : ""}`);
486
+ console.log(` ${actionNames.length} action${actionNames.length !== 1 ? "s" : ""}${actionNames.length > 0 ? ` (${actionNames.join(", ")})` : ""}`);
487
+ console.log(` ${dataNames.length} data fetcher${dataNames.length !== 1 ? "s" : ""}${dataNames.length > 0 ? ` (${dataNames.join(", ")})` : ""}`);
488
+ console.log("");
489
+ if (!tunnel) {
490
+ console.log(` \x1B[2mNext steps:\x1B[0m`);
491
+ console.log(` \x1B[36m1.\x1B[0m Open a view in your browser: ${baseUrl}/ui/${viewNames[0] || "yourView"}`);
492
+ console.log(` \x1B[36m2.\x1B[0m Connect an MCP client to: ${baseUrl}${mcpRoute}`);
493
+ console.log(` \x1B[36m3.\x1B[0m Expose for AI clients: \x1B[2mngrok http ${config.port}\x1B[0m or \x1B[2mcloudflared tunnel --url http://localhost:${config.port}\x1B[0m`);
494
+ console.log("");
495
+ } else {
496
+ console.log(` \x1B[2mNext steps:\x1B[0m`);
497
+ console.log(` \x1B[36m1.\x1B[0m Open a view in your browser: ${baseUrl}/ui/${viewNames[0] || "yourView"}`);
498
+ console.log(` \x1B[36m2.\x1B[0m Connect an MCP client to: ${tunnel.url}${mcpRoute}`);
499
+ console.log("");
500
+ }
501
+ }
502
+ resolve2({
503
+ close: async () => {
504
+ if (tunnel) {
505
+ await tunnel.close();
506
+ }
507
+ return new Promise((resolveClose) => {
508
+ httpServer.close(() => resolveClose());
509
+ });
510
+ }
511
+ });
512
+ });
513
+ });
514
+ }
515
+
516
+ export { createServer };
517
+ //# sourceMappingURL=server-B32JUAYX.js.map
518
+ //# sourceMappingURL=server-B32JUAYX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/detect-protocol.ts","../src/server/mcp.ts","../src/server/dev-proxy.ts","../src/server/routes.ts","../src/server/index.ts"],"names":["resolve"],"mappings":"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","file":"server-B32JUAYX.js","sourcesContent":["/**\n * Protocol detection utilities\n *\n * Provides utilities for detecting which protocol (MCP or OpenAI) to use\n * based on request headers or configuration.\n */\n\nimport type { Protocol } from '../types/protocol.js';\n\n/**\n * Request-like object for protocol detection\n */\ninterface RequestLike {\n headers: {\n [key: string]: string | string[] | undefined;\n };\n}\n\n/**\n * Detect protocol from request headers\n *\n * Detection logic:\n * 1. Check for OpenAI-specific Accept header (text/html+skybridge)\n * 2. Check for OpenAI-specific User-Agent patterns\n * 3. Check for X-OpenAI-* headers\n * 4. Fall back to configured default or 'mcp'\n *\n * @param req - Request object with headers\n * @param defaultProtocol - Default protocol if detection fails\n * @returns Detected protocol\n */\nexport function detectProtocol(\n req: RequestLike,\n defaultProtocol: Protocol = 'mcp'\n): Protocol {\n const headers = req.headers;\n\n // Check Accept header for OpenAI MIME type\n const accept = getHeader(headers, 'accept');\n if (accept && accept.includes('text/html+skybridge')) {\n return 'openai';\n }\n\n // Check for OpenAI-specific headers\n const openaiHeader = getHeader(headers, 'x-openai-client');\n if (openaiHeader) {\n return 'openai';\n }\n\n // Check User-Agent for ChatGPT patterns\n const userAgent = getHeader(headers, 'user-agent');\n if (userAgent && (userAgent.includes('ChatGPT') || userAgent.includes('OpenAI'))) {\n return 'openai';\n }\n\n return defaultProtocol;\n}\n\n/**\n * Get a header value as a string\n */\nfunction getHeader(\n headers: { [key: string]: string | string[] | undefined },\n name: string\n): string | undefined {\n const value = headers[name] || headers[name.toLowerCase()];\n if (Array.isArray(value)) {\n return value[0];\n }\n return value;\n}\n","import type { RequestHandler } from 'express';\nimport type { InternalTool } from '../types/tool.js';\nimport type { UIDefinition, Protocol } from '../types/protocol.js';\nimport { createAdapter } from '../adapters/index.js';\nimport { detectProtocol } from '../utils/detect-protocol.js';\n\n/**\n * UI resource for serving\n */\ninterface UIResource {\n name: string;\n uri: string;\n definition: UIDefinition;\n}\n\n/**\n * MCP handler configuration\n */\ninterface McpHandlerConfig {\n name: string;\n version: string;\n tools: Map<string, InternalTool>;\n uiResources: Map<string, UIResource>;\n executeTool: (toolName: string, input: unknown, metadata?: Record<string, unknown>) => Promise<unknown>;\n /** Default protocol if detection fails (default: 'mcp') */\n defaultProtocol?: Protocol | undefined;\n}\n\n/**\n * MCP JSON-RPC request structure\n */\ninterface JsonRpcRequest {\n jsonrpc: '2.0';\n id: string | number;\n method: string;\n params?: Record<string, unknown>;\n}\n\n/**\n * MCP JSON-RPC response structure\n */\ninterface JsonRpcResponse {\n jsonrpc: '2.0';\n id: string | number;\n result?: unknown;\n error?: {\n code: number;\n message: string;\n data?: unknown;\n };\n}\n\n/**\n * Create an Express handler for MCP JSON-RPC requests\n */\nexport function createMcpHandler(config: McpHandlerConfig): RequestHandler {\n return async (req, res) => {\n const request = req.body as JsonRpcRequest;\n\n if (request.jsonrpc !== '2.0') {\n res.status(400).json({\n jsonrpc: '2.0',\n id: request.id ?? null,\n error: { code: -32600, message: 'Invalid Request: Not JSON-RPC 2.0' },\n });\n return;\n }\n\n try {\n // Detect protocol from request headers\n const protocol = detectProtocol(req, config.defaultProtocol);\n const adapter = createAdapter(protocol);\n\n const result = await handleMethod(config, request.method, request.params ?? {}, adapter);\n const response: JsonRpcResponse = {\n jsonrpc: '2.0',\n id: request.id,\n result,\n };\n res.json(response);\n } catch (error) {\n const response: JsonRpcResponse = {\n jsonrpc: '2.0',\n id: request.id,\n error: {\n code: -32603,\n message: error instanceof Error ? error.message : 'Internal error',\n data: error instanceof Error ? { stack: error.stack } : undefined,\n },\n };\n res.json(response);\n }\n };\n}\n\n/**\n * Handle MCP method calls\n */\nasync function handleMethod(\n config: McpHandlerConfig,\n method: string,\n params: Record<string, unknown>,\n adapter: ReturnType<typeof createAdapter>\n): Promise<unknown> {\n switch (method) {\n case 'initialize':\n return {\n protocolVersion: '2025-11-21',\n capabilities: {\n tools: {},\n resources: {},\n },\n serverInfo: {\n name: config.name,\n version: config.version,\n },\n };\n\n case 'tools/list':\n return {\n tools: Array.from(config.tools.values())\n // Filter out app-only tools (they shouldn't be visible to the model)\n .filter((tool) => tool.visibility !== 'app')\n .map((tool) => {\n const uiUri = tool.ui ? getUIResourceUri(tool.name, tool.category) : undefined;\n const meta = adapter.buildToolMeta(tool, uiUri);\n\n return {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n annotations: meta.annotations,\n _meta: tool.ui ? meta._meta : undefined,\n };\n }),\n };\n\n case 'tools/call': {\n const toolName = params['name'] as string;\n const args = (params['arguments'] as Record<string, unknown>) ?? {};\n\n const result = await config.executeTool(toolName, args);\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result),\n },\n ],\n structuredContent: result,\n };\n }\n\n case 'resources/list':\n return {\n resources: Array.from(config.uiResources.values()).map((resource) => {\n const meta = adapter.buildUIResourceMeta(resource.definition);\n\n return {\n uri: resource.uri,\n name: resource.name,\n mimeType: meta.mimeType,\n _meta: meta._meta,\n };\n }),\n };\n\n case 'resources/read': {\n const uri = params['uri'] as string;\n const resource = config.uiResources.get(uri);\n\n if (!resource) {\n throw new Error(`Resource not found: ${uri}`);\n }\n\n // Read actual file content\n let htmlContent: string;\n const htmlPath = resource.definition.html;\n\n if (htmlPath.trim().startsWith('<')) {\n // Inline HTML\n htmlContent = htmlPath;\n } else {\n // File path - read content\n const fsPromises = await import('node:fs/promises');\n const pathModule = await import('node:path');\n const fullPath = pathModule.resolve(process.cwd(), htmlPath);\n htmlContent = await fsPromises.readFile(fullPath, 'utf-8');\n }\n\n return {\n contents: [\n {\n uri,\n mimeType: 'text/html',\n text: htmlContent,\n },\n ],\n };\n }\n\n case 'notifications/initialized':\n case 'ping':\n return {};\n\n default:\n throw new Error(`Unknown method: ${method}`);\n }\n}\n\n/**\n * Get the UI resource URI for a tool\n */\nfunction getUIResourceUri(toolName: string, category: string): string {\n // Extract the name without prefix\n const name = toolName.replace(/^(view|action|data|tool):/, '');\n return `pancake://ui/${category}/${name}`;\n}\n","import type { RequestHandler } from 'express';\nimport type { DevServerConfig } from '../types/app.js';\n\n/**\n * Check if running in development mode\n */\nexport function isDevelopment(): boolean {\n return process.env['NODE_ENV'] !== 'production';\n}\n\n/**\n * Create a proxy middleware for Vite dev server\n */\nexport function createViteProxy(config: DevServerConfig): RequestHandler {\n const vitePort = config.vitePort ?? 5173;\n const viteUrl = `http://localhost:${vitePort}`;\n\n // Paths to proxy to Vite\n const vitePaths = [\n '/@vite/',\n '/@react-refresh',\n '/assets/',\n '/src/',\n '/__vite_ping',\n '/@fs/',\n '/node_modules/.vite/',\n '/node_modules/.pnpm/', // pnpm store - Vite client imports from here\n ];\n\n return async (req, res, next) => {\n const shouldProxy = vitePaths.some((p) => req.path.startsWith(p));\n\n if (!shouldProxy) {\n next();\n return;\n }\n\n try {\n const targetUrl = `${viteUrl}${req.url}`;\n const response = await fetch(targetUrl, {\n method: req.method,\n headers: {\n ...Object.fromEntries(\n Object.entries(req.headers).filter(([key]) =>\n !['host', 'connection'].includes(key.toLowerCase())\n )\n ),\n } as Record<string, string>,\n });\n\n // Copy response headers\n response.headers.forEach((value, key) => {\n // Don't copy transfer-encoding as we're handling the body ourselves\n if (key.toLowerCase() !== 'transfer-encoding') {\n res.setHeader(key, value);\n }\n });\n\n res.status(response.status);\n\n // Get full response body and send it\n const body = await response.arrayBuffer();\n res.end(Buffer.from(body));\n } catch (error) {\n // Vite might not be running, pass through\n console.error(`[Pancake] Vite proxy failed for ${req.path}: ${(error as Error).message}`);\n next();\n }\n };\n}\n\n/**\n * Generate HTML template with Vite HMR support\n *\n * Note: Vite HMR client and React refresh use absolute URLs to Vite server\n * (they need direct WebSocket connection for HMR). View imports use relative\n * paths that go through the main server's proxy to avoid cross-origin issues.\n */\nexport function generateHMRTemplate(viewName: string): string {\n\n // All URLs use relative paths that go through our proxy\n // This ensures it works both from localhost AND from tunnel URLs\n return `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${viewName}</title>\n <!-- All Vite resources go through the proxy using relative URLs -->\n <script type=\"module\">\n import { injectIntoGlobalHook } from \"/@react-refresh\";\n injectIntoGlobalHook(window);\n window.$RefreshReg$ = () => {};\n window.$RefreshSig$ = () => (type) => type;\n window.__vite_plugin_react_preamble_installed__ = true;\n </script>\n <script type=\"module\" src=\"/@vite/client\"></script>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\">\n // Initialize Pancake client before loading the view\n import('/src/pancake-init.ts')\n .then(async (mod) => {\n if (mod.init) await mod.init();\n })\n .catch(() => {\n // pancake-init.ts might not exist, that's ok\n })\n .finally(() => {\n // Try each path and log which one fails and why\n const paths = [\n '/src/views/${viewName}/index.tsx',\n '/src/views/${viewName}.tsx',\n '/src/views/${viewName}/index.ts',\n '/src/views/${viewName}.ts'\n ];\n\n async function tryImport(index) {\n if (index >= paths.length) {\n document.body.innerHTML = '<pre style=\"color: red;\">Failed to load view: No valid module found</pre>';\n return;\n }\n const path = paths[index];\n try {\n console.log('[Pancake] Trying to import:', path);\n await import(path);\n console.log('[Pancake] Successfully imported:', path);\n } catch (err) {\n console.error('[Pancake] Failed to import ' + path + ':', err);\n // If this was the first path (the expected one), show the error\n if (index === 0) {\n document.body.innerHTML = '<pre style=\"color: red;\">Failed to load view ' + path + ':\\\\n\\\\n' + err.message + '\\\\n\\\\n' + (err.stack || '') + '</pre>';\n } else {\n tryImport(index + 1);\n }\n }\n }\n\n tryImport(0);\n });\n </script>\n </body>\n</html>`;\n}\n","import { Router } from 'express';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { UIDefinition } from '../types/protocol.js';\nimport type { DevServerConfig } from '../types/app.js';\nimport { generateHMRTemplate, isDevelopment } from './dev-proxy.js';\n\n/**\n * UI resource for serving\n */\ninterface UIResource {\n name: string;\n uri: string;\n definition: UIDefinition;\n}\n\n/**\n * Create routes for serving UI resources\n */\nexport function createUIRoutes(\n uiResources: Map<string, UIResource>,\n devServerConfig?: DevServerConfig\n): Router {\n const router = Router();\n\n // Serve UI by name\n router.get('/:category/:name', async (req, res) => {\n const { category, name } = req.params;\n const uri = `pancake://ui/${category}/${name}`;\n const resource = uiResources.get(uri);\n\n if (!resource) {\n res.status(404).json({ error: `UI not found: ${category}/${name}` });\n return;\n }\n\n try {\n const html = await getUIHtml(resource.definition, name, devServerConfig);\n res.type('html').send(html);\n } catch (error) {\n res.status(500).json({\n error: 'Failed to load UI',\n details: error instanceof Error ? error.message : String(error),\n });\n }\n });\n\n // Short form: /ui/:name (defaults to view category)\n router.get('/:name', async (req, res) => {\n const { name } = req.params;\n\n // Try view first, then action, then tool\n for (const category of ['view', 'action', 'tool']) {\n const uri = `pancake://ui/${category}/${name}`;\n const resource = uiResources.get(uri);\n\n if (resource) {\n try {\n const html = await getUIHtml(resource.definition, name, devServerConfig);\n res.type('html').send(html);\n return;\n } catch (error) {\n res.status(500).json({\n error: 'Failed to load UI',\n details: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n }\n }\n\n res.status(404).json({ error: `UI not found: ${name}` });\n });\n\n return router;\n}\n\n/**\n * Get HTML content for a UI definition\n */\nasync function getUIHtml(\n ui: UIDefinition,\n viewName: string,\n devServerConfig?: DevServerConfig\n): Promise<string> {\n // In development mode, generate HMR template\n if (isDevelopment() && devServerConfig) {\n return generateHMRTemplate(viewName);\n }\n\n // Check if html is inline (starts with <)\n if (ui.html.trim().startsWith('<')) {\n return ui.html;\n }\n\n // Otherwise, read from file\n const htmlPath = path.resolve(process.cwd(), ui.html);\n const content = await fs.readFile(htmlPath, 'utf-8');\n return content;\n}\n","import express, { type Express } from 'express';\nimport type { InternalTool } from '../types/tool.js';\nimport type { UIDefinition, Protocol } from '../types/protocol.js';\nimport type { EventMap, CorsConfig, DevServerConfig, TunnelConfig } from '../types/app.js';\nimport type { ToolContext } from '../types/context.js';\nimport { createMcpHandler } from './mcp.js';\nimport { createUIRoutes } from './routes.js';\nimport { createViteProxy, isDevelopment } from './dev-proxy.js';\nimport type { TunnelResult } from '../tunnel/index.js';\n\n/**\n * UI resource for serving\n */\ninterface UIResource {\n name: string;\n uri: string;\n definition: UIDefinition;\n}\n\n/**\n * Middleware chain interface\n */\ninterface MiddlewareChain {\n execute(\n ctx: { toolName: string; input: unknown; metadata: Record<string, unknown> },\n handler: () => Promise<unknown>\n ): Promise<unknown>;\n}\n\n/**\n * Event emitter interface\n */\ninterface TypedEventEmitter<TEvents extends { [K in keyof TEvents]: unknown }> {\n emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void;\n}\n\n/**\n * Server configuration\n */\nexport interface ServerConfig {\n name: string;\n version: string;\n tools: Map<string, InternalTool>;\n uiResources: Map<string, UIResource>;\n middlewareChain: MiddlewareChain;\n events: TypedEventEmitter<EventMap>;\n config?: {\n cors?: CorsConfig;\n debug?: boolean;\n serverRoute?: string;\n devServer?: DevServerConfig;\n protocol?: Protocol;\n tunnel?: TunnelConfig;\n };\n port: number;\n host: string;\n transport: 'http' | 'stdio' | 'sse';\n // Names for debug output\n viewNames?: string[];\n actionNames?: string[];\n dataNames?: string[];\n}\n\n/**\n * Generate a unique request ID\n */\nfunction generateRequestId(): string {\n return `req_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;\n}\n\n/**\n * Create the server instance\n */\nexport async function createServer(config: ServerConfig): Promise<{ close: () => Promise<void> }> {\n const app: Express = express();\n\n // Middleware\n app.use(express.json());\n\n // CORS handling\n if (config.config?.cors) {\n const corsConfig = config.config.cors;\n app.use((req, res, next) => {\n const origin = corsConfig.origin;\n if (origin === true) {\n res.setHeader('Access-Control-Allow-Origin', '*');\n } else if (typeof origin === 'string') {\n res.setHeader('Access-Control-Allow-Origin', origin);\n } else if (Array.isArray(origin)) {\n const requestOrigin = req.headers.origin;\n if (requestOrigin && origin.includes(requestOrigin)) {\n res.setHeader('Access-Control-Allow-Origin', requestOrigin);\n }\n }\n\n if (corsConfig.credentials) {\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n }\n if (corsConfig.methods) {\n res.setHeader('Access-Control-Allow-Methods', corsConfig.methods.join(', '));\n }\n if (corsConfig.allowedHeaders) {\n res.setHeader('Access-Control-Allow-Headers', corsConfig.allowedHeaders.join(', '));\n }\n\n if (req.method === 'OPTIONS') {\n res.status(204).end();\n return;\n }\n\n next();\n });\n }\n\n // Development mode: Vite HMR proxy\n if (isDevelopment() && config.config?.devServer) {\n app.use(createViteProxy(config.config.devServer));\n }\n\n // UI serving routes\n const uiRoutes = createUIRoutes(config.uiResources, config.config?.devServer);\n app.use('/ui', uiRoutes);\n\n // Tool execution helper\n const executeTool = async (\n toolName: string,\n input: unknown,\n metadata: Record<string, unknown> = {}\n ): Promise<unknown> => {\n const tool = config.tools.get(toolName);\n if (!tool) {\n throw new Error(`Unknown tool: ${toolName}`);\n }\n\n const ctx: ToolContext = {\n requestId: generateRequestId(),\n toolName,\n metadata,\n };\n\n const startTime = Date.now();\n\n try {\n const result = await config.middlewareChain.execute(\n { toolName, input, metadata },\n () => tool.handler(input, ctx)\n );\n\n config.events.emit('tool:success', {\n toolName,\n result,\n durationMs: Date.now() - startTime,\n });\n\n return result;\n } catch (error) {\n config.events.emit('tool:error', {\n toolName,\n error: error as Error,\n durationMs: Date.now() - startTime,\n });\n throw error;\n }\n };\n\n // MCP endpoint\n const mcpRoute = config.config?.serverRoute ?? '/mcp';\n const mcpHandler = createMcpHandler({\n name: config.name,\n version: config.version,\n tools: config.tools,\n uiResources: config.uiResources,\n executeTool,\n defaultProtocol: config.config?.protocol,\n });\n app.post(mcpRoute, mcpHandler);\n\n // Health check\n app.get('/health', (_req, res) => {\n res.json({ status: 'ok', name: config.name, version: config.version });\n });\n\n // Start server based on transport\n if (config.transport === 'stdio') {\n // Import and start stdio transport\n const { startStdioServer } = await import('./stdio.js');\n return startStdioServer({\n name: config.name,\n version: config.version,\n tools: config.tools,\n uiResources: config.uiResources,\n executeTool,\n protocol: config.config?.protocol,\n });\n }\n\n // HTTP server\n return new Promise((resolve) => {\n const httpServer = app.listen(config.port, config.host, async () => {\n const baseUrl = `http://localhost:${config.port}`;\n const viewNames = config.viewNames ?? [];\n const actionNames = config.actionNames ?? [];\n const dataNames = config.dataNames ?? [];\n\n // Start tunnel if configured\n let tunnel: TunnelResult | null = null;\n if (config.config?.tunnel) {\n const provider = config.config.tunnel.provider;\n const { isTunnelProviderAvailable, createTunnel } = await import('../tunnel/index.js');\n const isAvailable = await isTunnelProviderAvailable(provider);\n\n if (!isAvailable) {\n // Show clear warning when tunnel provider is not installed\n console.log('');\n console.log(` \\x1b[33m⚠\\x1b[0m Tunnel provider \"${provider}\" is not installed`);\n if (provider === 'cloudflare') {\n console.log(` Install with: \\x1b[1mbrew install cloudflared\\x1b[0m`);\n } else {\n console.log(` Install with: \\x1b[1mbrew install ngrok\\x1b[0m`);\n }\n console.log(` Continuing without tunnel...`);\n console.log('');\n } else {\n // Provider is available, try to start tunnel\n console.log(` \\x1b[36m→\\x1b[0m Starting ${provider} tunnel...`);\n try {\n const tunnelConfig: Parameters<typeof createTunnel>[0] = {\n provider,\n port: config.port,\n };\n if (config.config.tunnel.authtoken) {\n tunnelConfig.authtoken = config.config.tunnel.authtoken;\n }\n tunnel = await createTunnel(tunnelConfig);\n console.log(` \\x1b[32m✓\\x1b[0m Tunnel started: \\x1b[1m${tunnel.url}\\x1b[0m`);\n } catch (error) {\n console.log(` \\x1b[31m✗\\x1b[0m Tunnel failed: ${(error as Error).message}`);\n }\n }\n }\n\n if (config.config?.debug) {\n console.log('');\n console.log(` \\x1b[1m\\x1b[32m✓\\x1b[0m \\x1b[1m${config.name}\\x1b[0m is running`);\n console.log('');\n console.log(` \\x1b[2m➜\\x1b[0m \\x1b[1mServer:\\x1b[0m ${baseUrl}`);\n console.log(` \\x1b[2m➜\\x1b[0m \\x1b[1mMCP:\\x1b[0m ${baseUrl}${mcpRoute}`);\n if (viewNames.length > 0) {\n console.log(` \\x1b[2m➜\\x1b[0m \\x1b[1mViews:\\x1b[0m ${viewNames.map(v => `${baseUrl}/ui/${v}`).join('\\n ')}`);\n }\n \n // Display tunnel URL if available\n if (tunnel) {\n const providerName = config.config.tunnel?.provider === 'cloudflare' ? 'Cloudflare' : 'ngrok';\n console.log('');\n console.log(` \\x1b[1m\\x1b[35m⚡\\x1b[0m \\x1b[1mTunnel (${providerName}):\\x1b[0m`);\n console.log(` \\x1b[2m➜\\x1b[0m \\x1b[1mPublic:\\x1b[0m ${tunnel.url}`);\n console.log(` \\x1b[2m➜\\x1b[0m \\x1b[1mMCP:\\x1b[0m ${tunnel.url}${mcpRoute}`);\n console.log('');\n console.log(` \\x1b[33m⚠\\x1b[0m \\x1b[2mTunnel URL is ephemeral and will change on restart\\x1b[0m`);\n }\n \n console.log('');\n console.log(` \\x1b[2mRegistered:\\x1b[0m`);\n console.log(` ${viewNames.length} view${viewNames.length !== 1 ? 's' : ''}${viewNames.length > 0 ? ` (${viewNames.join(', ')})` : ''}`);\n console.log(` ${actionNames.length} action${actionNames.length !== 1 ? 's' : ''}${actionNames.length > 0 ? ` (${actionNames.join(', ')})` : ''}`);\n console.log(` ${dataNames.length} data fetcher${dataNames.length !== 1 ? 's' : ''}${dataNames.length > 0 ? ` (${dataNames.join(', ')})` : ''}`);\n console.log('');\n \n if (!tunnel) {\n console.log(` \\x1b[2mNext steps:\\x1b[0m`);\n console.log(` \\x1b[36m1.\\x1b[0m Open a view in your browser: ${baseUrl}/ui/${viewNames[0] || 'yourView'}`);\n console.log(` \\x1b[36m2.\\x1b[0m Connect an MCP client to: ${baseUrl}${mcpRoute}`);\n console.log(` \\x1b[36m3.\\x1b[0m Expose for AI clients: \\x1b[2mngrok http ${config.port}\\x1b[0m or \\x1b[2mcloudflared tunnel --url http://localhost:${config.port}\\x1b[0m`);\n console.log('');\n } else {\n console.log(` \\x1b[2mNext steps:\\x1b[0m`);\n console.log(` \\x1b[36m1.\\x1b[0m Open a view in your browser: ${baseUrl}/ui/${viewNames[0] || 'yourView'}`);\n console.log(` \\x1b[36m2.\\x1b[0m Connect an MCP client to: ${tunnel.url}${mcpRoute}`);\n console.log('');\n }\n }\n\n resolve({\n close: async () => {\n // Close tunnel first if active\n if (tunnel) {\n await tunnel.close();\n }\n return new Promise((resolveClose) => {\n httpServer.close(() => resolveClose());\n });\n },\n });\n });\n });\n}\n"]}