@brander/mcp-demo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * BranderUX Demo — MCP Integration Showcase (stdio transport)
4
+ *
5
+ * For local use with Claude Desktop. See api/mcp.ts for the HTTP transport (Vercel).
6
+ *
7
+ * Claude Desktop config:
8
+ * {
9
+ * "mcpServers": {
10
+ * "branderux-demo": {
11
+ * "command": "node",
12
+ * "args": ["/path/to/brander-mcp-demo/dist/index.js"],
13
+ * "env": {
14
+ * "BRANDER_PROJECT_ID": "your_project_id",
15
+ * "BRANDER_BETA_KEY": "bux_dp_your_key"
16
+ * }
17
+ * }
18
+ * }
19
+ * }
20
+ */
21
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * BranderUX Demo — MCP Integration Showcase (stdio transport)
4
+ *
5
+ * For local use with Claude Desktop. See api/mcp.ts for the HTTP transport (Vercel).
6
+ *
7
+ * Claude Desktop config:
8
+ * {
9
+ * "mcpServers": {
10
+ * "branderux-demo": {
11
+ * "command": "node",
12
+ * "args": ["/path/to/brander-mcp-demo/dist/index.js"],
13
+ * "env": {
14
+ * "BRANDER_PROJECT_ID": "your_project_id",
15
+ * "BRANDER_BETA_KEY": "bux_dp_your_key"
16
+ * }
17
+ * }
18
+ * }
19
+ * }
20
+ */
21
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
22
+ import { createServer } from "./create-server.js";
23
+ const server = await createServer();
24
+ const transport = new StdioServerTransport();
25
+ await server.connect(transport);
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCapabilityTools(server: McpServer): void;
@@ -0,0 +1,98 @@
1
+ import { z } from "zod";
2
+ import { INTEGRATION_METHODS, AI_CAPABILITIES } from "../data/integrations.js";
3
+ export function registerCapabilityTools(server) {
4
+ server.registerTool("get_integration_guide", {
5
+ description: "Get the integration guide for a specific BranderUX deployment method: SDK, MCP, or Embed. " +
6
+ "Returns step-by-step setup instructions, code examples, features, and requirements. " +
7
+ "After getting results, use generate_screen with a header for the integration method, " +
8
+ "details_data for setup steps and features, " +
9
+ "a form element for 'Get Started' configuration, " +
10
+ "a button element linking to documentation, " +
11
+ "and an alert element for any prerequisites or limitations.",
12
+ inputSchema: z.object({
13
+ method: z
14
+ .string()
15
+ .describe("Integration method: 'sdk' | 'mcp' | 'embed'"),
16
+ }),
17
+ }, async (input) => ({ content: [{ type: "text", text: handleGetIntegrationGuide(input) }] }));
18
+ server.registerTool("explore_ai_capabilities", {
19
+ description: "Explore BranderUX's AI capabilities: screen selection, query enhancement, AG-UI streaming, " +
20
+ "multi-provider support, click-to-query interactions, and A2UI flexible mode. " +
21
+ "Filter by category or status. " +
22
+ "After getting results, use generate_screen with a data_table element showing all capabilities, " +
23
+ "an item_grid for visual capability cards, " +
24
+ "and a chat_bubble summarizing how BranderUX's AI works.",
25
+ inputSchema: z.object({
26
+ category: z
27
+ .string()
28
+ .optional()
29
+ .describe("Category filter: 'Core AI' | 'Streaming' | 'Compatibility' | 'Interactivity' | 'Advanced'"),
30
+ status: z
31
+ .string()
32
+ .optional()
33
+ .describe("Status filter: 'ga' | 'beta' | 'coming-soon'"),
34
+ }),
35
+ }, async (input) => ({ content: [{ type: "text", text: handleExploreAICapabilities(input) }] }));
36
+ }
37
+ // ============================================================================
38
+ // HANDLERS
39
+ // ============================================================================
40
+ function handleGetIntegrationGuide(input) {
41
+ const method = input.method.toLowerCase();
42
+ const integration = INTEGRATION_METHODS.find((m) => m.slug === method || m.name.toLowerCase() === method);
43
+ if (!integration) {
44
+ return JSON.stringify({
45
+ error: `Integration method not found: ${method}. Available: sdk, mcp, embed`,
46
+ available: INTEGRATION_METHODS.map((m) => ({
47
+ slug: m.slug,
48
+ name: m.name,
49
+ tagline: m.tagline,
50
+ })),
51
+ });
52
+ }
53
+ // Also return a summary of all methods for comparison
54
+ const otherMethods = INTEGRATION_METHODS.filter((m) => m.slug !== integration.slug).map((m) => ({
55
+ name: m.name,
56
+ slug: m.slug,
57
+ tagline: m.tagline,
58
+ setupTime: m.setupTime,
59
+ adoptionRate: `${m.adoptionRate}%`,
60
+ }));
61
+ return JSON.stringify({
62
+ integration: {
63
+ ...integration,
64
+ adoptionRate: `${integration.adoptionRate}%`,
65
+ },
66
+ otherMethods,
67
+ });
68
+ }
69
+ function handleExploreAICapabilities(input) {
70
+ let results = [...AI_CAPABILITIES];
71
+ if (input.category && typeof input.category === "string") {
72
+ const cat = input.category.toLowerCase();
73
+ results = results.filter((c) => c.category.toLowerCase().includes(cat));
74
+ }
75
+ if (input.status && typeof input.status === "string") {
76
+ results = results.filter((c) => c.status === input.status);
77
+ }
78
+ const gaCount = AI_CAPABILITIES.filter((c) => c.status === "ga").length;
79
+ const betaCount = AI_CAPABILITIES.filter((c) => c.status === "beta").length;
80
+ return JSON.stringify({
81
+ total: results.length,
82
+ summary: {
83
+ totalCapabilities: AI_CAPABILITIES.length,
84
+ generallyAvailable: gaCount,
85
+ inBeta: betaCount,
86
+ categories: [...new Set(AI_CAPABILITIES.map((c) => c.category))],
87
+ },
88
+ capabilities: results.map((c) => ({
89
+ id: c.id,
90
+ name: c.name,
91
+ category: c.category,
92
+ description: c.description,
93
+ howItWorks: c.howItWorks,
94
+ technicalDetail: c.technicalDetail,
95
+ status: c.status,
96
+ })),
97
+ });
98
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerComponentTools(server: McpServer): void;
@@ -0,0 +1,109 @@
1
+ import { z } from "zod";
2
+ import { COMPONENTS } from "../data/components.js";
3
+ import { SCENARIOS } from "../data/scenarios.js";
4
+ export function registerComponentTools(server) {
5
+ server.registerTool("browse_components", {
6
+ description: "Browse BranderUX's 14 built-in UI component types (plus Custom Elements coming soon). " +
7
+ "Filter by category, status, or search by name. Returns component cards with popularity, " +
8
+ "usage stats, and example queries. " +
9
+ "After getting results, use generate_screen with item_grid element type to display components " +
10
+ "as a visual catalog, and optionally a stats_grid for summary metrics.",
11
+ inputSchema: z.object({
12
+ query: z
13
+ .string()
14
+ .optional()
15
+ .describe("Search text to match against component name or description"),
16
+ category: z
17
+ .string()
18
+ .optional()
19
+ .describe("Category filter: 'Structure & Navigation' | 'Data & Analytics' | 'Content & Media' | 'User Interaction' | 'Communication' | 'Customization'"),
20
+ status: z
21
+ .string()
22
+ .optional()
23
+ .describe("Status filter: 'stable' | 'beta' | 'new' | 'coming-soon'"),
24
+ limit: z.number().optional().describe("Maximum results to return (default 15)"),
25
+ }),
26
+ }, async (input) => ({ content: [{ type: "text", text: handleBrowseComponents(input) }] }));
27
+ server.registerTool("get_component_details", {
28
+ description: "Get detailed information about a specific BranderUX UI component by ID or name. " +
29
+ "Returns full component data including props, use cases, and usage statistics. " +
30
+ "Also shows which example scenarios use this component. " +
31
+ "After getting results, use generate_screen with a header for the component name, " +
32
+ "a details_data element for props and configuration, " +
33
+ "a chat_bubble for the description and use case explanation, " +
34
+ "and an alert element if the component is in beta or coming-soon status.",
35
+ inputSchema: z.object({
36
+ component_id: z
37
+ .string()
38
+ .describe("Component ID (e.g. 'comp-header') or name (e.g. 'Header', 'DataTable', 'StatsGrid')"),
39
+ }),
40
+ }, async (input) => ({ content: [{ type: "text", text: handleGetComponentDetails(input) }] }));
41
+ }
42
+ // ============================================================================
43
+ // HANDLERS
44
+ // ============================================================================
45
+ function handleBrowseComponents(input) {
46
+ let results = [...COMPONENTS];
47
+ if (input.query && typeof input.query === "string") {
48
+ const q = input.query.toLowerCase();
49
+ results = results.filter((c) => c.name.toLowerCase().includes(q) ||
50
+ c.description.toLowerCase().includes(q) ||
51
+ c.elementType.toLowerCase().includes(q));
52
+ }
53
+ if (input.category && typeof input.category === "string") {
54
+ const cat = input.category.toLowerCase();
55
+ results = results.filter((c) => c.category.toLowerCase().includes(cat));
56
+ }
57
+ if (input.status && typeof input.status === "string") {
58
+ results = results.filter((c) => c.status === input.status);
59
+ }
60
+ const limit = typeof input.limit === "number" ? input.limit : 15;
61
+ results = results.slice(0, limit);
62
+ const stableCount = COMPONENTS.filter((c) => c.status === "stable").length;
63
+ const totalUsage = COMPONENTS.reduce((sum, c) => sum + c.usageCount, 0);
64
+ return JSON.stringify({
65
+ total: results.length,
66
+ summary: {
67
+ totalComponents: COMPONENTS.length,
68
+ stableComponents: stableCount,
69
+ totalMonthlyUsage: totalUsage,
70
+ mostPopular: COMPONENTS.reduce((a, b) => a.popularityScore > b.popularityScore ? a : b).name,
71
+ },
72
+ components: results.map((c) => ({
73
+ id: c.id,
74
+ name: c.name,
75
+ elementType: c.elementType,
76
+ category: c.category,
77
+ description: c.description,
78
+ popularityScore: c.popularityScore,
79
+ usageCount: c.usageCount,
80
+ status: c.status,
81
+ bestFor: c.bestFor,
82
+ exampleQuery: c.exampleQuery,
83
+ })),
84
+ });
85
+ }
86
+ function handleGetComponentDetails(input) {
87
+ const id = input.component_id;
88
+ const component = COMPONENTS.find((c) => c.id === id ||
89
+ c.name.toLowerCase() === id.toLowerCase() ||
90
+ c.elementType === id.toLowerCase() ||
91
+ c.name.toLowerCase().includes(id.toLowerCase()));
92
+ if (!component) {
93
+ return JSON.stringify({ error: `Component not found: ${id}` });
94
+ }
95
+ // Find scenarios that use this component
96
+ const relatedScenarios = SCENARIOS.filter((s) => s.componentsUsed.includes(component.elementType)).map((s) => ({
97
+ id: s.id,
98
+ name: s.name,
99
+ industry: s.industry,
100
+ integrationMethod: s.integrationMethod,
101
+ }));
102
+ return JSON.stringify({
103
+ component: {
104
+ ...component,
105
+ relatedScenarios,
106
+ scenarioCount: relatedScenarios.length,
107
+ },
108
+ });
109
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerPlatformTools(server: McpServer): void;
@@ -0,0 +1,123 @@
1
+ import { z } from "zod";
2
+ import { PLATFORM_ANALYTICS, PLATFORM_FEATURES } from "../data/platform-analytics.js";
3
+ export function registerPlatformTools(server) {
4
+ server.registerTool("get_platform_analytics", {
5
+ description: "Get BranderUX platform analytics and performance metrics. Returns project counts, " +
6
+ "API performance, component popularity, integration method breakdown, and growth trends. " +
7
+ "After getting results, use generate_screen with: a header for the period, " +
8
+ "stats_grid for key metrics (active projects, screen generations, avg response time, uptime), " +
9
+ "line_chart for monthly growth trend, " +
10
+ "pie_chart for integration method or AI provider breakdown, " +
11
+ "and bar_chart for component popularity ranking.",
12
+ inputSchema: z.object({
13
+ period: z
14
+ .string()
15
+ .optional()
16
+ .describe("Time period (e.g. 'Q1 2026'). Defaults to Q1 2026."),
17
+ metric: z
18
+ .string()
19
+ .optional()
20
+ .describe("Focus on a specific metric: 'overview' | 'growth' | 'components' | 'integrations' | 'providers' | 'screens'"),
21
+ }),
22
+ }, async (input) => ({
23
+ content: [{ type: "text", text: handleGetPlatformAnalytics(input) }],
24
+ }));
25
+ server.registerTool("get_feature_overview", {
26
+ description: "Get an overview of BranderUX platform features and capabilities. " +
27
+ "Filter by category or status. Returns feature descriptions with availability. " +
28
+ "After getting results, use generate_screen with a header for 'BranderUX Platform', " +
29
+ "an item_grid showing features as cards, " +
30
+ "a stats_grid for capability highlights (total features, GA count, highlights), " +
31
+ "and a chat_bubble summarizing the platform's value proposition.",
32
+ inputSchema: z.object({
33
+ category: z
34
+ .string()
35
+ .optional()
36
+ .describe("Category filter: 'Core' | 'AI' | 'Customization' | 'Developer'"),
37
+ status: z
38
+ .string()
39
+ .optional()
40
+ .describe("Status filter: 'ga' | 'beta' | 'coming-soon'"),
41
+ }),
42
+ }, async (input) => ({
43
+ content: [{ type: "text", text: handleGetFeatureOverview(input) }],
44
+ }));
45
+ }
46
+ // ============================================================================
47
+ // HANDLERS
48
+ // ============================================================================
49
+ function handleGetPlatformAnalytics(input) {
50
+ const data = PLATFORM_ANALYTICS;
51
+ if (input.metric && typeof input.metric === "string") {
52
+ const metric = input.metric.toLowerCase();
53
+ if (metric === "overview") {
54
+ return JSON.stringify({ period: data.period, overview: data.overview });
55
+ }
56
+ if (metric === "growth") {
57
+ return JSON.stringify({ period: data.period, monthlyGrowth: data.monthlyGrowth });
58
+ }
59
+ if (metric === "components") {
60
+ return JSON.stringify({
61
+ period: data.period,
62
+ componentPopularity: data.componentPopularity,
63
+ });
64
+ }
65
+ if (metric === "integrations") {
66
+ return JSON.stringify({
67
+ period: data.period,
68
+ integrationBreakdown: data.integrationBreakdown,
69
+ });
70
+ }
71
+ if (metric === "providers") {
72
+ return JSON.stringify({
73
+ period: data.period,
74
+ aiProviderBreakdown: data.aiProviderBreakdown,
75
+ });
76
+ }
77
+ if (metric === "screens") {
78
+ return JSON.stringify({
79
+ period: data.period,
80
+ screenTypeUsage: data.screenTypeUsage,
81
+ });
82
+ }
83
+ }
84
+ // Return all analytics
85
+ return JSON.stringify(data);
86
+ }
87
+ function handleGetFeatureOverview(input) {
88
+ let results = [...PLATFORM_FEATURES];
89
+ if (input.category && typeof input.category === "string") {
90
+ const cat = input.category.toLowerCase();
91
+ results = results.filter((f) => f.category.toLowerCase().includes(cat));
92
+ }
93
+ if (input.status && typeof input.status === "string") {
94
+ results = results.filter((f) => f.status === input.status);
95
+ }
96
+ const gaCount = PLATFORM_FEATURES.filter((f) => f.status === "ga").length;
97
+ const betaCount = PLATFORM_FEATURES.filter((f) => f.status === "beta").length;
98
+ const comingSoonCount = PLATFORM_FEATURES.filter((f) => f.status === "coming-soon").length;
99
+ const highlightCount = PLATFORM_FEATURES.filter((f) => f.highlight).length;
100
+ return JSON.stringify({
101
+ total: results.length,
102
+ summary: {
103
+ totalFeatures: PLATFORM_FEATURES.length,
104
+ generallyAvailable: gaCount,
105
+ inBeta: betaCount,
106
+ comingSoon: comingSoonCount,
107
+ highlights: highlightCount,
108
+ categories: [...new Set(PLATFORM_FEATURES.map((f) => f.category))],
109
+ },
110
+ valueProposition: "BranderUX is an AI-UX middleware platform that transforms AI responses into branded, " +
111
+ "interactive UI components. Instead of plain text, your users see dashboards, charts, " +
112
+ "tables, and forms — all in your brand, all clickable, all streaming in real-time. " +
113
+ "Integrate with one line of code. Works with Claude, GPT, and Gemini.",
114
+ features: results.map((f) => ({
115
+ id: f.id,
116
+ name: f.name,
117
+ category: f.category,
118
+ description: f.description,
119
+ status: f.status,
120
+ highlight: f.highlight,
121
+ })),
122
+ });
123
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerScenarioTools(server: McpServer): void;
@@ -0,0 +1,113 @@
1
+ import { z } from "zod";
2
+ import { SCENARIOS } from "../data/scenarios.js";
3
+ import { COMPONENTS } from "../data/components.js";
4
+ export function registerScenarioTools(server) {
5
+ server.registerTool("browse_scenarios", {
6
+ description: "Browse example implementation scenarios showing what you can build with BranderUX. " +
7
+ "Filter by industry, integration method, or AI provider. " +
8
+ "Returns scenario summaries with components used and expected performance metrics. " +
9
+ "After getting results, use generate_screen with a data_table element to display scenarios " +
10
+ "in a sortable table, and optionally a stats_grid for aggregate metrics.",
11
+ inputSchema: z.object({
12
+ industry: z
13
+ .string()
14
+ .optional()
15
+ .describe("Industry filter (e.g. 'Fintech', 'Healthcare', 'E-commerce', 'Education', 'Insurance')"),
16
+ integration_method: z
17
+ .string()
18
+ .optional()
19
+ .describe("Integration method filter: 'SDK' | 'MCP' | 'Embed'"),
20
+ ai_provider: z
21
+ .string()
22
+ .optional()
23
+ .describe("AI provider filter: 'Anthropic' | 'OpenAI' | 'Google'"),
24
+ limit: z.number().optional().describe("Maximum results (default 10)"),
25
+ }),
26
+ }, async (input) => ({ content: [{ type: "text", text: handleBrowseScenarios(input) }] }));
27
+ server.registerTool("get_scenario_details", {
28
+ description: "Get detailed information about a specific BranderUX implementation scenario by ID or name. " +
29
+ "Returns full scenario details including components used, screen configurations, " +
30
+ "and expected performance improvements. " +
31
+ "After getting results, use generate_screen with a header for the scenario name, " +
32
+ "details_data for implementation details and metrics, " +
33
+ "a bar_chart comparing expected before/after performance improvements, " +
34
+ "and item_card elements for each component used in the scenario.",
35
+ inputSchema: z.object({
36
+ scenario_id: z
37
+ .string()
38
+ .describe("Scenario ID (e.g. 'sc-001') or name (e.g. 'Fintech Transaction Dashboard')"),
39
+ }),
40
+ }, async (input) => ({ content: [{ type: "text", text: handleGetScenarioDetails(input) }] }));
41
+ }
42
+ // ============================================================================
43
+ // HANDLERS
44
+ // ============================================================================
45
+ function handleBrowseScenarios(input) {
46
+ let results = [...SCENARIOS];
47
+ if (input.industry && typeof input.industry === "string") {
48
+ const industry = input.industry.toLowerCase();
49
+ results = results.filter((s) => s.industry.toLowerCase().includes(industry));
50
+ }
51
+ if (input.integration_method && typeof input.integration_method === "string") {
52
+ const method = input.integration_method.toUpperCase();
53
+ results = results.filter((s) => s.integrationMethod === method);
54
+ }
55
+ if (input.ai_provider && typeof input.ai_provider === "string") {
56
+ const provider = input.ai_provider.toLowerCase();
57
+ results = results.filter((s) => s.aiProvider.toLowerCase().includes(provider));
58
+ }
59
+ const limit = typeof input.limit === "number" ? input.limit : 10;
60
+ // Sort by engagement lift descending
61
+ results.sort((a, b) => b.expectedResults.engagementLift - a.expectedResults.engagementLift);
62
+ results = results.slice(0, limit);
63
+ const avgEngagement = SCENARIOS.reduce((sum, s) => sum + s.expectedResults.engagementLift, 0) / SCENARIOS.length;
64
+ const avgResponseTime = SCENARIOS.reduce((sum, s) => sum + s.expectedResults.responseTimeMs, 0) / SCENARIOS.length;
65
+ return JSON.stringify({
66
+ total: results.length,
67
+ summary: {
68
+ totalScenarios: SCENARIOS.length,
69
+ avgEngagementLift: `${Math.round(avgEngagement)}%`,
70
+ avgResponseTime: `${Math.round(avgResponseTime)}ms`,
71
+ industries: [...new Set(SCENARIOS.map((s) => s.industry))],
72
+ },
73
+ scenarios: results.map((s) => ({
74
+ id: s.id,
75
+ name: s.name,
76
+ industry: s.industry,
77
+ integrationMethod: s.integrationMethod,
78
+ aiProvider: s.aiProvider,
79
+ componentsUsed: s.componentsUsed,
80
+ screensConfigured: s.screensConfigured,
81
+ engagementLift: `${s.expectedResults.engagementLift}%`,
82
+ responseTimeMs: s.expectedResults.responseTimeMs,
83
+ satisfaction: s.expectedResults.userSatisfaction,
84
+ monthlyInteractions: s.expectedResults.monthlyInteractions,
85
+ tags: s.tags,
86
+ })),
87
+ });
88
+ }
89
+ function handleGetScenarioDetails(input) {
90
+ const id = input.scenario_id;
91
+ const scenario = SCENARIOS.find((s) => s.id === id ||
92
+ s.name.toLowerCase().includes(id.toLowerCase()));
93
+ if (!scenario) {
94
+ return JSON.stringify({ error: `Scenario not found: ${id}` });
95
+ }
96
+ // Enrich with component details
97
+ const componentDetails = scenario.componentsUsed
98
+ .map((elementType) => COMPONENTS.find((c) => c.elementType === elementType))
99
+ .filter(Boolean)
100
+ .map((c) => ({
101
+ name: c.name,
102
+ elementType: c.elementType,
103
+ category: c.category,
104
+ description: c.description,
105
+ }));
106
+ return JSON.stringify({
107
+ scenario: {
108
+ ...scenario,
109
+ componentDetails,
110
+ componentCount: componentDetails.length,
111
+ },
112
+ });
113
+ }
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@brander/mcp-demo",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "BranderUX Demo — Explore BranderUX capabilities through branded UI",
6
+ "bin": {
7
+ "brander-mcp-demo": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist/"
11
+ ],
12
+ "keywords": [
13
+ "mcp",
14
+ "branderux",
15
+ "claude",
16
+ "chatgpt",
17
+ "ai",
18
+ "branded-ui",
19
+ "model-context-protocol"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "dev": "tsx --watch src/index.ts",
24
+ "start": "node dist/index.js"
25
+ },
26
+ "dependencies": {
27
+ "@brander/mcp-tools": "^0.2.0",
28
+ "@modelcontextprotocol/sdk": "^1.0.0",
29
+ "zod": "^4.3.6"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^20.0.0",
33
+ "@vercel/node": "^5.0.0",
34
+ "tsx": "^4.0.0",
35
+ "typescript": "^5.5.4"
36
+ }
37
+ }