create-skybridge 0.0.0-dev.b0a1d24 → 0.0.0-dev.b0a6e24

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.
Files changed (45) hide show
  1. package/dist/index.js +101 -64
  2. package/dist/index.test.js +11 -1
  3. package/package.json +8 -7
  4. package/template/AGENTS.md +1 -0
  5. package/template/README.md +39 -22
  6. package/template/_gitignore +6 -0
  7. package/template/alpic.json +1 -2
  8. package/template/node_modules/.bin/alpic +21 -0
  9. package/template/node_modules/.bin/sb +21 -0
  10. package/template/node_modules/.bin/skybridge +21 -0
  11. package/template/node_modules/.bin/tsc +2 -2
  12. package/template/node_modules/.bin/tsserver +2 -2
  13. package/template/node_modules/.bin/tsx +2 -2
  14. package/template/node_modules/.bin/vite +2 -2
  15. package/template/package.json +18 -25
  16. package/template/src/components/ball.tsx +22 -0
  17. package/template/src/helpers.ts +4 -0
  18. package/template/src/index.css +152 -0
  19. package/template/{server/src → src}/server.ts +8 -8
  20. package/template/src/views/magic-8-ball.tsx +10 -0
  21. package/template/tsconfig.json +7 -19
  22. package/template/{web/vite.config.ts → vite.config.ts} +0 -2
  23. package/template/node_modules/.bin/mcp-inspector +0 -21
  24. package/template/node_modules/.bin/nodemon +0 -21
  25. package/template/node_modules/.bin/shx +0 -21
  26. package/template/nodemon.json +0 -5
  27. package/template/server/src/index.ts +0 -39
  28. package/template/server/src/middleware.ts +0 -54
  29. package/template/tsconfig.server.json +0 -11
  30. package/template/web/src/helpers.ts +0 -4
  31. package/template/web/src/index.css +0 -31
  32. package/template/web/src/widgets/magic-8-ball.tsx +0 -24
  33. package/template-ecom/README.md +0 -86
  34. package/template-ecom/alpic.json +0 -4
  35. package/template-ecom/nodemon.json +0 -5
  36. package/template-ecom/package.json +0 -40
  37. package/template-ecom/server/src/index.ts +0 -39
  38. package/template-ecom/server/src/middleware.ts +0 -54
  39. package/template-ecom/server/src/server.ts +0 -73
  40. package/template-ecom/tsconfig.json +0 -23
  41. package/template-ecom/tsconfig.server.json +0 -11
  42. package/template-ecom/web/src/helpers.ts +0 -4
  43. package/template-ecom/web/src/index.css +0 -178
  44. package/template-ecom/web/src/widgets/ecom-carousel.tsx +0 -109
  45. package/template-ecom/web/vite.config.ts +0 -15
@@ -1,54 +0,0 @@
1
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
2
- import type { NextFunction, Request, Response } from "express";
3
-
4
- import type { McpServer } from "skybridge/server";
5
-
6
- export const mcp =
7
- (server: McpServer) =>
8
- async (req: Request, res: Response, next: NextFunction) => {
9
- // Only handle requests to the /mcp path
10
- if (req.path !== "/mcp") {
11
- return next();
12
- }
13
-
14
- if (req.method === "POST") {
15
- try {
16
- const transport = new StreamableHTTPServerTransport({
17
- sessionIdGenerator: undefined,
18
- });
19
-
20
- res.on("close", () => {
21
- transport.close();
22
- });
23
-
24
- await server.connect(transport);
25
-
26
- await transport.handleRequest(req, res, req.body);
27
- } catch (error) {
28
- console.error("Error handling MCP request:", error);
29
- if (!res.headersSent) {
30
- res.status(500).json({
31
- jsonrpc: "2.0",
32
- error: {
33
- code: -32603,
34
- message: "Internal server error",
35
- },
36
- id: null,
37
- });
38
- }
39
- }
40
- } else if (req.method === "GET" || req.method === "DELETE") {
41
- res.writeHead(405).end(
42
- JSON.stringify({
43
- jsonrpc: "2.0",
44
- error: {
45
- code: -32000,
46
- message: "Method not allowed.",
47
- },
48
- id: null,
49
- }),
50
- );
51
- } else {
52
- next();
53
- }
54
- };
@@ -1,73 +0,0 @@
1
- import { McpServer } from "skybridge/server";
2
- import { z } from "zod";
3
-
4
- interface Product {
5
- id: number;
6
- title: string;
7
- price: number;
8
- description: string;
9
- category: string;
10
- image: string;
11
- rating: {
12
- rate: number;
13
- count: number;
14
- };
15
- }
16
-
17
- const server = new McpServer(
18
- {
19
- name: "ecom-carousel-app",
20
- version: "0.0.1",
21
- },
22
- { capabilities: {} },
23
- ).registerWidget(
24
- "ecom-carousel",
25
- {
26
- description: "E-commerce Product Carousel",
27
- },
28
- {
29
- description: "Display a carousel of products from the store.",
30
- inputSchema: {
31
- category: z
32
- .enum(["electronics", "jewelery", "men's clothing", "women's clothing"])
33
- .optional()
34
- .describe("Filter by product category"),
35
- maxPrice: z.number().optional().describe("Maximum price filter"),
36
- },
37
- },
38
- async ({ category, maxPrice }) => {
39
- try {
40
- const response = await fetch("https://fakestoreapi.com/products");
41
- if (!response.ok) {
42
- throw new Error(`API request failed: ${response.status}`);
43
- }
44
-
45
- const products: Product[] = await response.json();
46
- const filtered: Product[] = [];
47
-
48
- for (const product of products) {
49
- if (category && product.category !== category) {
50
- continue;
51
- }
52
- if (maxPrice !== undefined && product.price > maxPrice) {
53
- continue;
54
- }
55
- filtered.push(product);
56
- }
57
-
58
- return {
59
- structuredContent: { products: filtered },
60
- content: [{ type: "text", text: JSON.stringify(filtered) }],
61
- isError: false,
62
- };
63
- } catch (error) {
64
- return {
65
- content: [{ type: "text", text: `Error: ${error}` }],
66
- isError: true,
67
- };
68
- }
69
- },
70
- );
71
-
72
- export default server;
73
- export type AppType = typeof server;
@@ -1,23 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
- "jsx": "react-jsx",
8
-
9
- "strict": true,
10
- "skipLibCheck": true,
11
- "esModuleInterop": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "verbatimModuleSyntax": true,
14
-
15
- "noUnusedLocals": true,
16
- "noUnusedParameters": true,
17
- "noFallthroughCasesInSwitch": true,
18
-
19
- "noEmit": true
20
- },
21
- "include": ["server/src", "web/src", "web/vite.config.ts"],
22
- "exclude": ["dist", "node_modules"]
23
- }
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "noEmit": false,
5
- "outDir": "server/dist",
6
- "sourceMap": true,
7
- "declaration": true
8
- },
9
- "include": ["server/src"],
10
- "exclude": ["dist", "node_modules"]
11
- }
@@ -1,4 +0,0 @@
1
- import { generateHelpers } from "skybridge/web";
2
- import type { AppType } from "../../server/src/server";
3
-
4
- export const { useToolInfo } = generateHelpers<AppType>();
@@ -1,178 +0,0 @@
1
- .message {
2
- display: flex;
3
- justify-content: center;
4
- align-items: center;
5
- margin: 1rem;
6
- padding: 1rem;
7
- font-family: sans-serif;
8
- border-radius: 8px;
9
- }
10
-
11
- .message.light {
12
- color: #555;
13
- background: rgba(255, 255, 255, 0.9);
14
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
15
- }
16
-
17
- .message.dark {
18
- color: #ccc;
19
- background: rgba(40, 40, 40, 0.95);
20
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
21
- }
22
-
23
- .container {
24
- display: flex;
25
- flex-direction: column;
26
- height: auto;
27
- }
28
-
29
- .carousel {
30
- display: flex;
31
- gap: 1rem;
32
- overflow-x: auto;
33
- padding: 1rem;
34
- scroll-snap-type: x mandatory;
35
- margin-left: 1rem;
36
- }
37
-
38
- .product-card {
39
- flex: 0 0 150px;
40
- border: none;
41
- border-radius: 8px;
42
- overflow: hidden;
43
- scroll-snap-align: start;
44
- cursor: pointer;
45
- transition:
46
- transform 0.2s,
47
- box-shadow 0.2s;
48
- padding: 0;
49
- text-align: left;
50
- }
51
-
52
- .product-card.light {
53
- background: #fff;
54
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
55
- }
56
-
57
- .product-card.light:hover {
58
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
59
- }
60
-
61
- .product-card.dark {
62
- background: #2a2a2a;
63
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
64
- }
65
-
66
- .product-card.dark:hover {
67
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
68
- }
69
-
70
- .product-card:hover {
71
- transform: translateY(-2px);
72
- }
73
-
74
- .product-card.selected {
75
- border-bottom: 5px solid mediumseagreen;
76
- }
77
-
78
- .product-image {
79
- width: 100%;
80
- height: 120px;
81
- object-fit: contain;
82
- padding: 0.5rem;
83
- display: block;
84
- box-sizing: border-box;
85
- }
86
-
87
- .product-image.light {
88
- background: #f9f9f9;
89
- }
90
-
91
- .product-image.dark {
92
- background: #333;
93
- }
94
-
95
- .product-info {
96
- padding: 0.75rem;
97
- }
98
-
99
- .product-title {
100
- font-size: 0.75rem;
101
- font-family: sans-serif;
102
- overflow: hidden;
103
- text-overflow: ellipsis;
104
- white-space: nowrap;
105
- }
106
-
107
- .product-title.light {
108
- color: #333;
109
- }
110
-
111
- .product-title.dark {
112
- color: #eee;
113
- }
114
-
115
- .product-price {
116
- font-size: 0.875rem;
117
- font-family: sans-serif;
118
- font-weight: bold;
119
- color: mediumseagreen;
120
- margin-top: 0.25rem;
121
- }
122
-
123
- .product-detail {
124
- margin: 0 1rem 1rem;
125
- padding: 1rem;
126
- border-radius: 8px;
127
- font-family: sans-serif;
128
- }
129
-
130
- .product-detail.light {
131
- background: #fff;
132
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
133
- }
134
-
135
- .product-detail.dark {
136
- background: #2a2a2a;
137
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
138
- }
139
-
140
- .detail-title {
141
- font-size: 1rem;
142
- font-weight: bold;
143
- margin-bottom: 0.5rem;
144
- }
145
-
146
- .detail-title.light {
147
- color: #333;
148
- }
149
-
150
- .detail-title.dark {
151
- color: #eee;
152
- }
153
-
154
- .detail-rating {
155
- font-size: 0.875rem;
156
- margin-bottom: 0.5rem;
157
- }
158
-
159
- .detail-rating.light {
160
- color: #666;
161
- }
162
-
163
- .detail-rating.dark {
164
- color: #aaa;
165
- }
166
-
167
- .detail-description {
168
- font-size: 0.8rem;
169
- line-height: 1.4;
170
- }
171
-
172
- .detail-description.light {
173
- color: #555;
174
- }
175
-
176
- .detail-description.dark {
177
- color: #bbb;
178
- }
@@ -1,109 +0,0 @@
1
- import "@/index.css";
2
-
3
- import { useState } from "react";
4
- import { mountWidget, useLayout, useUser } from "skybridge/web";
5
- import { useToolInfo } from "../helpers.js";
6
-
7
- const translations: Record<string, Record<string, string>> = {
8
- en: {
9
- loading: "Loading products...",
10
- noProducts: "No product found",
11
- },
12
- fr: {
13
- loading: "Chargement des produits...",
14
- noProducts: "Aucun produit trouvé",
15
- },
16
- es: {
17
- loading: "Cargando productos...",
18
- noProducts: "No se encontraron productos",
19
- },
20
- de: {
21
- loading: "Produkte werden geladen...",
22
- noProducts: "Keine Produkte gefunden",
23
- },
24
- };
25
-
26
- function EcomCarousel() {
27
- const { theme } = useLayout();
28
- const { locale } = useUser();
29
-
30
- const lang = locale?.split("-")[0] ?? "en";
31
-
32
- function translate(key: string) {
33
- return translations[lang]?.[key] ?? translations.en[key];
34
- }
35
-
36
- const { output, isPending } = useToolInfo<"ecom-carousel">();
37
- type Product = NonNullable<typeof output>["products"][number];
38
- const [selected, setSelected] = useState<Product | null>(null);
39
-
40
- function cn(...classes: string[]) {
41
- return [theme, ...classes].filter(Boolean).join(" ");
42
- }
43
-
44
- if (isPending) {
45
- return (
46
- <div className={cn("container")}>
47
- <div className={cn("message")}>{translate("loading")}</div>
48
- </div>
49
- );
50
- }
51
-
52
- if (!output || output.products.length === 0) {
53
- return (
54
- <div className={cn("container")}>
55
- <div className={cn("message")}>{translate("noProducts")}</div>
56
- </div>
57
- );
58
- }
59
-
60
- const activeProduct = selected ?? output.products[0];
61
-
62
- return (
63
- <div className={cn("container")}>
64
- <div className={cn("carousel")}>
65
- {output.products.map((product) => (
66
- <button
67
- type="button"
68
- key={product.id}
69
- className={cn(
70
- "product-card",
71
- activeProduct?.id === product.id ? "selected" : "",
72
- )}
73
- onClick={() =>
74
- setSelected(selected?.id === product.id ? null : product)
75
- }
76
- >
77
- <img
78
- src={product.image}
79
- alt={product.title}
80
- className={cn("product-image")}
81
- />
82
- <div className={cn("product-info")}>
83
- <div className={cn("product-title")}>{product.title}</div>
84
- <div className={cn("product-price")}>
85
- ${product.price.toFixed(2)}
86
- </div>
87
- </div>
88
- </button>
89
- ))}
90
- </div>
91
- {activeProduct && (
92
- <div className={cn("product-detail")}>
93
- <div className={cn("detail-title")}>{activeProduct.title}</div>
94
- <div className={cn("detail-rating")}>
95
- ⭐ {activeProduct.rating.rate} ({activeProduct.rating.count}{" "}
96
- reviews)
97
- </div>
98
- <div className={cn("detail-description")}>
99
- {activeProduct.description}
100
- </div>
101
- </div>
102
- )}
103
- </div>
104
- );
105
- }
106
-
107
- export default EcomCarousel;
108
-
109
- mountWidget(<EcomCarousel />);
@@ -1,15 +0,0 @@
1
- import path from "node:path";
2
- import react from "@vitejs/plugin-react";
3
- import { skybridge } from "skybridge/web";
4
- import { defineConfig } from "vite";
5
-
6
- // https://vite.dev/config/
7
- export default defineConfig({
8
- plugins: [skybridge(), react()],
9
- root: __dirname,
10
- resolve: {
11
- alias: {
12
- "@": path.resolve(__dirname, "./src"),
13
- },
14
- },
15
- });