@brander/mcp-tools 0.2.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.
- package/README.md +126 -0
- package/dist/app/index.html +8406 -0
- package/dist/server/brand/brand-loader.d.ts +26 -0
- package/dist/server/brand/brand-loader.d.ts.map +1 -0
- package/dist/server/brand/brand-loader.js +89 -0
- package/dist/server/brand/brand-types.d.ts +93 -0
- package/dist/server/brand/brand-types.d.ts.map +1 -0
- package/dist/server/brand/brand-types.js +32 -0
- package/dist/server/config/server-config.d.ts +16 -0
- package/dist/server/config/server-config.d.ts.map +1 -0
- package/dist/server/config/server-config.js +71 -0
- package/dist/server/config/types.d.ts +21 -0
- package/dist/server/config/types.d.ts.map +1 -0
- package/dist/server/config/types.js +4 -0
- package/dist/server/elements/click-behaviors.d.ts +13 -0
- package/dist/server/elements/click-behaviors.d.ts.map +1 -0
- package/dist/server/elements/click-behaviors.js +71 -0
- package/dist/server/elements/element-functions.d.ts +24 -0
- package/dist/server/elements/element-functions.d.ts.map +1 -0
- package/dist/server/elements/element-functions.js +167 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +30 -0
- package/dist/server/lib-entry.d.ts +16 -0
- package/dist/server/lib-entry.d.ts.map +1 -0
- package/dist/server/lib-entry.js +14 -0
- package/dist/server/register.d.ts +35 -0
- package/dist/server/register.d.ts.map +1 -0
- package/dist/server/register.js +49 -0
- package/dist/server/resource/html-loader.d.ts +9 -0
- package/dist/server/resource/html-loader.d.ts.map +1 -0
- package/dist/server/resource/html-loader.js +20 -0
- package/dist/server/resource/resource-registry.d.ts +15 -0
- package/dist/server/resource/resource-registry.d.ts.map +1 -0
- package/dist/server/resource/resource-registry.js +45 -0
- package/dist/server/server.d.ts +13 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +36 -0
- package/dist/server/tools/element-definitions.d.ts +15 -0
- package/dist/server/tools/element-definitions.d.ts.map +1 -0
- package/dist/server/tools/element-definitions.js +51 -0
- package/dist/server/tools/generate-screen-schema.d.ts +262 -0
- package/dist/server/tools/generate-screen-schema.d.ts.map +1 -0
- package/dist/server/tools/generate-screen-schema.js +227 -0
- package/dist/server/tools/tool-handler.d.ts +11 -0
- package/dist/server/tools/tool-handler.d.ts.map +1 -0
- package/dist/server/tools/tool-handler.js +83 -0
- package/dist/server/tools/tool-registry.d.ts +15 -0
- package/dist/server/tools/tool-registry.d.ts.map +1 -0
- package/dist/server/tools/tool-registry.js +30 -0
- package/dist/server/types/element-types.d.ts +37 -0
- package/dist/server/types/element-types.d.ts.map +1 -0
- package/dist/server/types/element-types.js +22 -0
- package/dist/server/types/mcp-types.d.ts +52 -0
- package/dist/server/types/mcp-types.d.ts.map +1 -0
- package/dist/server/types/mcp-types.js +4 -0
- package/package.json +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-screen-schema.d.ts","sourceRoot":"","sources":["../../../src/tools/generate-screen-schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA+OxB,2DAA2D;AAC3D,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIpC,CAAC;AAEH,2CAA2C;AAC3C,eAAO,MAAM,2BAA2B,QAO8H,CAAC"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-contained generate_screen Zod schema + tool description for the MCP server.
|
|
3
|
+
*
|
|
4
|
+
* All 14 element prop schemas are defined here so the compiled dist/server can run
|
|
5
|
+
* as a standalone Node.js server without depending on @brander/elements at runtime.
|
|
6
|
+
* (Avoids CJS/ESM interop issues — brander-elements is CJS but mcp-tools is ESM.)
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// ELEMENT PROP SCHEMAS (14 element types)
|
|
11
|
+
// ============================================================================
|
|
12
|
+
const headerPropsSchema = z.object({
|
|
13
|
+
title: z.string().describe("Main page title"),
|
|
14
|
+
subtitle: z.string().optional().describe("Page description or subtitle"),
|
|
15
|
+
});
|
|
16
|
+
const chatBubblePropsSchema = z.object({
|
|
17
|
+
text: z.string().describe("Response text content"),
|
|
18
|
+
markdown: z.boolean().optional().describe("Whether to render as markdown"),
|
|
19
|
+
});
|
|
20
|
+
const statsGridPropsSchema = z.object({
|
|
21
|
+
stats: z
|
|
22
|
+
.array(z.object({
|
|
23
|
+
id: z.string().describe("Unique stat identifier"),
|
|
24
|
+
title: z.string().describe("Stat title"),
|
|
25
|
+
value: z.string().describe("Main value"),
|
|
26
|
+
period: z.string().optional().describe("Period (This Month, This Year, etc.)"),
|
|
27
|
+
trend: z
|
|
28
|
+
.object({
|
|
29
|
+
direction: z.enum(["up", "down", "neutral"]),
|
|
30
|
+
percentage: z.string().describe("Trend percentage"),
|
|
31
|
+
})
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Trend direction"),
|
|
34
|
+
subtitle: z.string().optional().describe("Additional context"),
|
|
35
|
+
}))
|
|
36
|
+
.describe("Array of statistics (generate 4 if not requested differently)"),
|
|
37
|
+
});
|
|
38
|
+
const dataTablePropsSchema = z.object({
|
|
39
|
+
title: z.string().optional().describe("Table title"),
|
|
40
|
+
columns: z
|
|
41
|
+
.array(z.object({
|
|
42
|
+
key: z.string().describe("Column key"),
|
|
43
|
+
label: z.string().describe("Column label"),
|
|
44
|
+
type: z.enum(["text", "number", "status", "date", "currency", "boolean"]),
|
|
45
|
+
sortable: z.boolean().optional().describe("Whether column is sortable"),
|
|
46
|
+
filterable: z.boolean().optional().describe("Whether column is filterable"),
|
|
47
|
+
}))
|
|
48
|
+
.describe("Column definitions"),
|
|
49
|
+
rows: z
|
|
50
|
+
.array(z.looseObject({ id: z.string().describe("Unique row identifier") }))
|
|
51
|
+
.describe("Data rows"),
|
|
52
|
+
pageSize: z.number().optional().describe("Number of rows per page"),
|
|
53
|
+
});
|
|
54
|
+
const lineChartPropsSchema = z.object({
|
|
55
|
+
title: z.string().describe("Chart title"),
|
|
56
|
+
labels: z.array(z.string()).describe("X-axis labels (e.g., ['Jan', 'Feb', 'Mar'])"),
|
|
57
|
+
data: z.array(z.number()).describe("Chart data"),
|
|
58
|
+
});
|
|
59
|
+
const pieChartPropsSchema = z.object({
|
|
60
|
+
title: z.string().describe("Chart title"),
|
|
61
|
+
data: z
|
|
62
|
+
.array(z.object({
|
|
63
|
+
label: z.string().describe("Segment label"),
|
|
64
|
+
value: z.number().describe("Segment value"),
|
|
65
|
+
color: z.string().optional().describe("Segment color"),
|
|
66
|
+
}))
|
|
67
|
+
.describe("Pie chart segments"),
|
|
68
|
+
});
|
|
69
|
+
const barChartPropsSchema = z.object({
|
|
70
|
+
id: z.string().describe("Unique chart identifier"),
|
|
71
|
+
title: z.string().optional().describe("Chart title"),
|
|
72
|
+
categories: z.array(z.string()).describe("X-axis category labels"),
|
|
73
|
+
series: z
|
|
74
|
+
.array(z.object({
|
|
75
|
+
name: z.string().describe("Series name"),
|
|
76
|
+
data: z.array(z.number()).describe("Data values"),
|
|
77
|
+
}))
|
|
78
|
+
.describe("Data series (minimum 2 series)"),
|
|
79
|
+
stacked: z.boolean().optional().describe("Stack bars"),
|
|
80
|
+
showLegend: z.boolean().optional().describe("Display legend"),
|
|
81
|
+
});
|
|
82
|
+
const itemGridPropsSchema = z.object({
|
|
83
|
+
title: z.string().optional().describe("Grid title"),
|
|
84
|
+
items: z
|
|
85
|
+
.array(z.object({
|
|
86
|
+
id: z.string().describe("Item ID"),
|
|
87
|
+
title: z.string().describe("Item name"),
|
|
88
|
+
description: z.string().optional().describe("Item description"),
|
|
89
|
+
price: z.string().describe("Item price"),
|
|
90
|
+
image: z
|
|
91
|
+
.string()
|
|
92
|
+
.optional()
|
|
93
|
+
.describe("REAL image URL (e.g., https://images.unsplash.com/...)"),
|
|
94
|
+
category: z.string().optional().describe("Item category"),
|
|
95
|
+
rating: z.number().optional().describe("Rating score"),
|
|
96
|
+
stock: z.number().optional().describe("Stock quantity"),
|
|
97
|
+
}))
|
|
98
|
+
.describe("Grid items"),
|
|
99
|
+
pageSize: z.number().optional().describe("Number of items per page"),
|
|
100
|
+
});
|
|
101
|
+
const itemCardPropsSchema = z.object({
|
|
102
|
+
id: z.string().describe("Item ID"),
|
|
103
|
+
title: z.string().describe("Item name"),
|
|
104
|
+
subtitle: z.string().optional().describe("Brief description"),
|
|
105
|
+
image: z.string().optional().describe("Image URL"),
|
|
106
|
+
price: z.string().optional().describe("Item price with currency symbol (e.g., '99.99$')"),
|
|
107
|
+
category: z.string().optional().describe("Item category"),
|
|
108
|
+
stock: z.number().optional().describe("Number of items in stock"),
|
|
109
|
+
rating: z.number().optional().describe("Item rating (0-5)"),
|
|
110
|
+
description: z.string().optional().describe("Detailed description"),
|
|
111
|
+
});
|
|
112
|
+
const imagePropsSchema = z.object({
|
|
113
|
+
src: z.string().describe("Image source URL"),
|
|
114
|
+
alt: z.string().describe("Alt text for accessibility"),
|
|
115
|
+
title: z.string().optional().describe("Image title"),
|
|
116
|
+
caption: z.string().optional().describe("Image caption"),
|
|
117
|
+
width: z.number().optional().describe("Image width"),
|
|
118
|
+
height: z.number().optional().describe("Image height"),
|
|
119
|
+
});
|
|
120
|
+
const detailsDataPropsSchema = z.object({
|
|
121
|
+
items: z
|
|
122
|
+
.array(z.object({
|
|
123
|
+
id: z.string().describe("Item ID"),
|
|
124
|
+
title: z.string().describe("Section title"),
|
|
125
|
+
value: z.string().describe("Section value"),
|
|
126
|
+
type: z.enum(["text", "price", "status"]).describe("Section type"),
|
|
127
|
+
category: z.string().describe("Section category (REQUIRED for logical grouping)"),
|
|
128
|
+
color: z
|
|
129
|
+
.enum(["default", "primary", "secondary", "error", "info", "success", "warning"])
|
|
130
|
+
.optional()
|
|
131
|
+
.describe("Section color"),
|
|
132
|
+
variant: z.enum(["outlined", "filled"]).optional().describe("Section variant"),
|
|
133
|
+
size: z.enum(["small", "medium"]).optional().describe("Section size"),
|
|
134
|
+
}))
|
|
135
|
+
.describe("Data sections — minimum 4 per category if not otherwise requested"),
|
|
136
|
+
});
|
|
137
|
+
const formPropsSchema = z.object({
|
|
138
|
+
title: z.string().describe("Form title"),
|
|
139
|
+
description: z.string().optional().describe("Form description"),
|
|
140
|
+
fields: z
|
|
141
|
+
.array(z.object({
|
|
142
|
+
id: z.string().describe("Unique field ID"),
|
|
143
|
+
name: z.string().describe("Field name"),
|
|
144
|
+
label: z.string().describe("Field label"),
|
|
145
|
+
type: z.enum([
|
|
146
|
+
"text",
|
|
147
|
+
"email",
|
|
148
|
+
"date",
|
|
149
|
+
"amount",
|
|
150
|
+
"number",
|
|
151
|
+
"tel",
|
|
152
|
+
"select",
|
|
153
|
+
"textarea",
|
|
154
|
+
"checkbox",
|
|
155
|
+
]),
|
|
156
|
+
required: z.boolean().optional().describe("Is field required"),
|
|
157
|
+
placeholder: z.string().optional().describe("Placeholder text"),
|
|
158
|
+
options: z.array(z.object({ value: z.string(), label: z.string() })).optional(),
|
|
159
|
+
}))
|
|
160
|
+
.describe("Form fields"),
|
|
161
|
+
submitButton: z
|
|
162
|
+
.object({
|
|
163
|
+
label: z.string().optional().describe("Button text"),
|
|
164
|
+
action: z.string().optional().describe("Submit action"),
|
|
165
|
+
})
|
|
166
|
+
.optional(),
|
|
167
|
+
});
|
|
168
|
+
const buttonPropsSchema = z.object({
|
|
169
|
+
id: z.string().describe("Unique button identifier"),
|
|
170
|
+
label: z.string().describe("Button text"),
|
|
171
|
+
variant: z.enum(["primary", "secondary", "tertiary"]).describe("Button style variant"),
|
|
172
|
+
type: z.enum(["action", "link"]).describe("Button behavior type"),
|
|
173
|
+
action: z.string().optional().describe("Query to send to AI (required if type is action)"),
|
|
174
|
+
url: z.string().optional().describe("External URL (required if type is link)"),
|
|
175
|
+
size: z.enum(["small", "medium", "large"]).optional().describe("Button size"),
|
|
176
|
+
disabled: z.boolean().optional().describe("Disabled state"),
|
|
177
|
+
startIcon: z.string().optional().describe("Icon name for start position"),
|
|
178
|
+
endIcon: z.string().optional().describe("Icon name for end position"),
|
|
179
|
+
});
|
|
180
|
+
const alertPropsSchema = z.object({
|
|
181
|
+
id: z.string().describe("Unique alert identifier"),
|
|
182
|
+
severity: z.enum(["success", "error", "warning", "info"]).describe("Alert severity level"),
|
|
183
|
+
title: z.string().optional().describe("Optional alert title/heading"),
|
|
184
|
+
message: z.string().describe("Main alert message"),
|
|
185
|
+
variant: z.enum(["standard", "outlined"]).optional().describe("Alert style variant"),
|
|
186
|
+
closeable: z.boolean().optional().describe("Show close button"),
|
|
187
|
+
showIcon: z.boolean().optional().describe("Display severity icon"),
|
|
188
|
+
actionText: z.string().optional().describe("Optional action button text"),
|
|
189
|
+
actionQuery: z.string().optional().describe("Query to send when action button clicked"),
|
|
190
|
+
});
|
|
191
|
+
// ============================================================================
|
|
192
|
+
// SCREEN SCHEMA — discriminated union of all 14 element types
|
|
193
|
+
// ============================================================================
|
|
194
|
+
const clickQuery = z
|
|
195
|
+
.string()
|
|
196
|
+
.optional()
|
|
197
|
+
.describe("Optional query to send when this element is clicked");
|
|
198
|
+
const screenElementSchema = z.discriminatedUnion("elementType", [
|
|
199
|
+
z.object({ elementType: z.literal("header"), props: headerPropsSchema, clickQuery }),
|
|
200
|
+
z.object({ elementType: z.literal("chat-bubble"), props: chatBubblePropsSchema, clickQuery }),
|
|
201
|
+
z.object({ elementType: z.literal("stats-grid"), props: statsGridPropsSchema, clickQuery }),
|
|
202
|
+
z.object({ elementType: z.literal("data-table"), props: dataTablePropsSchema, clickQuery }),
|
|
203
|
+
z.object({ elementType: z.literal("line-chart"), props: lineChartPropsSchema, clickQuery }),
|
|
204
|
+
z.object({ elementType: z.literal("pie-chart"), props: pieChartPropsSchema, clickQuery }),
|
|
205
|
+
z.object({ elementType: z.literal("bar-chart"), props: barChartPropsSchema, clickQuery }),
|
|
206
|
+
z.object({ elementType: z.literal("item-grid"), props: itemGridPropsSchema, clickQuery }),
|
|
207
|
+
z.object({ elementType: z.literal("item-card"), props: itemCardPropsSchema, clickQuery }),
|
|
208
|
+
z.object({ elementType: z.literal("image"), props: imagePropsSchema, clickQuery }),
|
|
209
|
+
z.object({ elementType: z.literal("details-data"), props: detailsDataPropsSchema, clickQuery }),
|
|
210
|
+
z.object({ elementType: z.literal("form"), props: formPropsSchema, clickQuery }),
|
|
211
|
+
z.object({ elementType: z.literal("button"), props: buttonPropsSchema, clickQuery }),
|
|
212
|
+
z.object({ elementType: z.literal("alert"), props: alertPropsSchema, clickQuery }),
|
|
213
|
+
]);
|
|
214
|
+
/** Full input schema for the MCP `generate_screen` tool */
|
|
215
|
+
export const generateScreenInputSchema = z.object({
|
|
216
|
+
elements: z
|
|
217
|
+
.array(screenElementSchema)
|
|
218
|
+
.describe("Ordered array of elements to render. Each element has an elementType and props."),
|
|
219
|
+
});
|
|
220
|
+
/** Tool description for generate_screen */
|
|
221
|
+
export const GENERATE_SCREEN_DESCRIPTION = "**REQUIRED FOR ALL VISUAL RESPONSES** - Render branded, interactive UI components. " +
|
|
222
|
+
"You MUST use this tool instead of writing React code for dashboards, data tables, charts, stats, or any visual content. " +
|
|
223
|
+
"Provide an ordered array of elements: for dashboards use header + stats-grid + data-table + charts; " +
|
|
224
|
+
"for simple text use chat-bubble. For clickable elements, add a `clickQuery` with placeholders [field] for dynamic values. " +
|
|
225
|
+
"Example: clickQuery: 'Show detailed analytics for [title] including sales trends and inventory'. " +
|
|
226
|
+
"Use [title], [id], [name] placeholders that will be filled when user clicks. DO NOT write React code - use this tool. " +
|
|
227
|
+
"Available elements: header, chat-bubble, stats-grid, data-table, line-chart, pie-chart, bar-chart, item-grid, item-card, image, details-data, form, button, alert.";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool handler — creates the MCP handler for the single generate_screen tool.
|
|
3
|
+
*/
|
|
4
|
+
import { ProjectConfig } from "../brand/brand-types.js";
|
|
5
|
+
import { ToolHandlerResult, ApiConfig } from "../types/mcp-types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Create the handler for the generate_screen tool.
|
|
8
|
+
* Accepts an array of elements and returns them as a composed screen.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createScreenHandler(projectConfig: ProjectConfig, apiConfig?: ApiConfig): (input: Record<string, unknown>) => ToolHandlerResult;
|
|
11
|
+
//# sourceMappingURL=tool-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-handler.d.ts","sourceRoot":"","sources":["../../../src/tools/tool-handler.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAKxD,OAAO,EAAE,iBAAiB,EAAiB,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAmBpF;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,aAAa,EAC5B,SAAS,CAAC,EAAE,SAAS,GACpB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,iBAAiB,CAoEvD"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool handler — creates the MCP handler for the single generate_screen tool.
|
|
3
|
+
*/
|
|
4
|
+
import { getClickBehavior } from "../elements/click-behaviors.js";
|
|
5
|
+
import { buildContentSummary } from "../elements/element-functions.js";
|
|
6
|
+
import { RESOURCE_URI } from "../resource/resource-registry.js";
|
|
7
|
+
import { ElementType } from "../types/element-types.js";
|
|
8
|
+
import { getElementDefinition } from "./element-definitions.js";
|
|
9
|
+
/** Build _meta with resource URI and optional CSP connect domains */
|
|
10
|
+
function buildMeta(apiConfig) {
|
|
11
|
+
const meta = {
|
|
12
|
+
ui: { resourceUri: RESOURCE_URI },
|
|
13
|
+
"ui/resourceUri": RESOURCE_URI,
|
|
14
|
+
};
|
|
15
|
+
if (apiConfig) {
|
|
16
|
+
meta.ui.csp = {
|
|
17
|
+
connectDomains: [new URL(apiConfig.apiBaseUrl).origin],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return meta;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create the handler for the generate_screen tool.
|
|
24
|
+
* Accepts an array of elements and returns them as a composed screen.
|
|
25
|
+
*/
|
|
26
|
+
export function createScreenHandler(projectConfig, apiConfig) {
|
|
27
|
+
const meta = buildMeta(apiConfig);
|
|
28
|
+
const { brandSettings } = projectConfig;
|
|
29
|
+
return (input) => {
|
|
30
|
+
const rawElements = input.elements || [];
|
|
31
|
+
if (rawElements.length === 0) {
|
|
32
|
+
return {
|
|
33
|
+
content: [{ type: "text", text: "Failed to render screen: no elements provided." }],
|
|
34
|
+
structuredContent: {
|
|
35
|
+
elementType: ElementType.SCREEN,
|
|
36
|
+
elements: [],
|
|
37
|
+
brandSettings,
|
|
38
|
+
projectSettings: projectConfig.settings,
|
|
39
|
+
screenVisibility: projectConfig.screenVisibility,
|
|
40
|
+
customScreens: projectConfig.customScreens,
|
|
41
|
+
apiConfig,
|
|
42
|
+
},
|
|
43
|
+
_meta: meta,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Build screen elements with click behaviors
|
|
47
|
+
const elements = rawElements.map((raw) => {
|
|
48
|
+
const elType = raw.elementType;
|
|
49
|
+
const def = getElementDefinition(elType);
|
|
50
|
+
const behavior = getClickBehavior(elType);
|
|
51
|
+
return {
|
|
52
|
+
elementType: elType,
|
|
53
|
+
props: raw.props,
|
|
54
|
+
clickQuery: raw.clickQuery, // AI-generated query (preferred)
|
|
55
|
+
clickBehavior: {
|
|
56
|
+
queryTemplate: behavior?.queryTemplate ?? null,
|
|
57
|
+
entityName: def?.name ?? raw.elementType,
|
|
58
|
+
}, // Fallback template system
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
// Build text summary — per-element summaries joined
|
|
62
|
+
const summaries = elements.map((el) => {
|
|
63
|
+
const def = getElementDefinition(el.elementType);
|
|
64
|
+
return buildContentSummary(el.elementType, el.props) || def?.name || el.elementType;
|
|
65
|
+
});
|
|
66
|
+
const summary = elements.length === 1
|
|
67
|
+
? summaries[0]
|
|
68
|
+
: `Rendered screen with ${elements.length} elements:\n${summaries.map((s) => ` - ${s}`).join("\n")}`;
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: "text", text: summary }],
|
|
71
|
+
structuredContent: {
|
|
72
|
+
elementType: ElementType.SCREEN,
|
|
73
|
+
elements,
|
|
74
|
+
brandSettings,
|
|
75
|
+
projectSettings: projectConfig.settings,
|
|
76
|
+
screenVisibility: projectConfig.screenVisibility,
|
|
77
|
+
customScreens: projectConfig.customScreens,
|
|
78
|
+
apiConfig,
|
|
79
|
+
},
|
|
80
|
+
_meta: meta,
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool registration — registers the single generate_screen tool with the MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Uses the high-level McpServer.registerTool() API for composability — customers
|
|
5
|
+
* can register their own tools alongside BranderUX tools on the same server.
|
|
6
|
+
*/
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { ProjectConfig } from "../brand/brand-types.js";
|
|
9
|
+
import { ApiConfig } from "../types/mcp-types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Register the BranderUX generate_screen tool with the MCP server.
|
|
12
|
+
* Single tool that renders 1-N branded elements in a composed screen.
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerTools(server: McpServer, projectConfig: ProjectConfig, apiConfig?: ApiConfig): void;
|
|
15
|
+
//# sourceMappingURL=tool-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../../src/tools/tool-registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAMlD;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,EACjB,aAAa,EAAE,aAAa,EAC5B,SAAS,CAAC,EAAE,SAAS,GACpB,IAAI,CAuBN"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool registration — registers the single generate_screen tool with the MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Uses the high-level McpServer.registerTool() API for composability — customers
|
|
5
|
+
* can register their own tools alongside BranderUX tools on the same server.
|
|
6
|
+
*/
|
|
7
|
+
import { generateScreenInputSchema, GENERATE_SCREEN_DESCRIPTION, } from "./generate-screen-schema.js";
|
|
8
|
+
import { createScreenHandler } from "./tool-handler.js";
|
|
9
|
+
import { RESOURCE_URI } from "../resource/resource-registry.js";
|
|
10
|
+
const TOOL_NAME = "generate_screen";
|
|
11
|
+
/**
|
|
12
|
+
* Register the BranderUX generate_screen tool with the MCP server.
|
|
13
|
+
* Single tool that renders 1-N branded elements in a composed screen.
|
|
14
|
+
*/
|
|
15
|
+
export function registerTools(server, projectConfig, apiConfig) {
|
|
16
|
+
const handler = createScreenHandler(projectConfig, apiConfig);
|
|
17
|
+
server.registerTool(TOOL_NAME, {
|
|
18
|
+
description: GENERATE_SCREEN_DESCRIPTION,
|
|
19
|
+
inputSchema: generateScreenInputSchema,
|
|
20
|
+
_meta: {
|
|
21
|
+
ui: { resourceUri: RESOURCE_URI },
|
|
22
|
+
"ui/resourceUri": RESOURCE_URI,
|
|
23
|
+
},
|
|
24
|
+
}, async (args) => {
|
|
25
|
+
// The SDK validates + parses against generateScreenInputSchema,
|
|
26
|
+
// but our handler expects Record<string, unknown>
|
|
27
|
+
return handler(args);
|
|
28
|
+
});
|
|
29
|
+
console.error(`Registered BranderUX tool: ${TOOL_NAME} ` + `(14 element types, resource: ${RESOURCE_URI})`);
|
|
30
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone Element types — no imports from main app.
|
|
3
|
+
* Mirrors types/element-types.ts ElementType enum.
|
|
4
|
+
*/
|
|
5
|
+
export declare enum ElementType {
|
|
6
|
+
HEADER = "header",
|
|
7
|
+
STATS_GRID = "stats-grid",
|
|
8
|
+
DATA_TABLE = "data-table",
|
|
9
|
+
LINE_CHART = "line-chart",
|
|
10
|
+
PIE_CHART = "pie-chart",
|
|
11
|
+
ITEM_GRID = "item-grid",
|
|
12
|
+
ITEM_CARD = "item-card",
|
|
13
|
+
IMAGE = "image",
|
|
14
|
+
DETAILS_DATA = "details-data",
|
|
15
|
+
CHAT_BUBBLE = "chat-bubble",
|
|
16
|
+
FORM = "form",
|
|
17
|
+
BUTTON = "button",
|
|
18
|
+
BAR_CHART = "bar-chart",
|
|
19
|
+
ALERT = "alert",
|
|
20
|
+
SCREEN = "screen"
|
|
21
|
+
}
|
|
22
|
+
export interface ToolSchema {
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
input_schema: {
|
|
26
|
+
type: "object";
|
|
27
|
+
properties: Record<string, unknown>;
|
|
28
|
+
required?: string[];
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface ElementDefinition {
|
|
32
|
+
id: ElementType;
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
toolSchema: ToolSchema;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=element-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-types.d.ts","sourceRoot":"","sources":["../../../src/types/element-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,oBAAY,WAAW;IACrB,MAAM,WAAW;IACjB,UAAU,eAAe;IACzB,UAAU,eAAe;IACzB,UAAU,eAAe;IACzB,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,KAAK,UAAU;IACf,YAAY,iBAAiB;IAC7B,WAAW,gBAAgB;IAC3B,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,SAAS,cAAc;IACvB,KAAK,UAAU;IACf,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,WAAW,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;CACxB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone Element types — no imports from main app.
|
|
3
|
+
* Mirrors types/element-types.ts ElementType enum.
|
|
4
|
+
*/
|
|
5
|
+
export var ElementType;
|
|
6
|
+
(function (ElementType) {
|
|
7
|
+
ElementType["HEADER"] = "header";
|
|
8
|
+
ElementType["STATS_GRID"] = "stats-grid";
|
|
9
|
+
ElementType["DATA_TABLE"] = "data-table";
|
|
10
|
+
ElementType["LINE_CHART"] = "line-chart";
|
|
11
|
+
ElementType["PIE_CHART"] = "pie-chart";
|
|
12
|
+
ElementType["ITEM_GRID"] = "item-grid";
|
|
13
|
+
ElementType["ITEM_CARD"] = "item-card";
|
|
14
|
+
ElementType["IMAGE"] = "image";
|
|
15
|
+
ElementType["DETAILS_DATA"] = "details-data";
|
|
16
|
+
ElementType["CHAT_BUBBLE"] = "chat-bubble";
|
|
17
|
+
ElementType["FORM"] = "form";
|
|
18
|
+
ElementType["BUTTON"] = "button";
|
|
19
|
+
ElementType["BAR_CHART"] = "bar-chart";
|
|
20
|
+
ElementType["ALERT"] = "alert";
|
|
21
|
+
ElementType["SCREEN"] = "screen";
|
|
22
|
+
})(ElementType || (ElementType = {}));
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP-specific helper types for the BranderUX MCP server.
|
|
3
|
+
*/
|
|
4
|
+
import { ElementType } from "./element-types.js";
|
|
5
|
+
import { BrandSettings, ProjectSettings, ScreenVisibilityMap, CustomScreen } from "../brand/brand-types.js";
|
|
6
|
+
/** API configuration for app-side query enhancement */
|
|
7
|
+
export interface ApiConfig {
|
|
8
|
+
apiBaseUrl: string;
|
|
9
|
+
betaKey: string;
|
|
10
|
+
projectId: string;
|
|
11
|
+
}
|
|
12
|
+
/** Screen element entry within a composed screen */
|
|
13
|
+
export interface ScreenElement {
|
|
14
|
+
elementType: ElementType;
|
|
15
|
+
props: Record<string, unknown>;
|
|
16
|
+
clickQuery?: string;
|
|
17
|
+
clickBehavior: {
|
|
18
|
+
queryTemplate: string | null;
|
|
19
|
+
entityName: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** structuredContent shape — always a screen with 1-N elements */
|
|
23
|
+
export interface ScreenStructuredContent {
|
|
24
|
+
elementType: ElementType.SCREEN;
|
|
25
|
+
elements: ScreenElement[];
|
|
26
|
+
brandSettings: BrandSettings;
|
|
27
|
+
projectSettings?: ProjectSettings;
|
|
28
|
+
screenVisibility?: ScreenVisibilityMap;
|
|
29
|
+
customScreens?: CustomScreen[];
|
|
30
|
+
apiConfig?: ApiConfig;
|
|
31
|
+
/** Index signature required by MCP SDK CallToolResult.structuredContent */
|
|
32
|
+
[key: string]: unknown;
|
|
33
|
+
}
|
|
34
|
+
/** Tool handler result shape matching MCP SDK CallToolResult */
|
|
35
|
+
export interface ToolHandlerResult {
|
|
36
|
+
content: Array<{
|
|
37
|
+
type: "text";
|
|
38
|
+
text: string;
|
|
39
|
+
}>;
|
|
40
|
+
structuredContent: ScreenStructuredContent;
|
|
41
|
+
_meta: {
|
|
42
|
+
ui: {
|
|
43
|
+
resourceUri: string;
|
|
44
|
+
};
|
|
45
|
+
/** Legacy key for older hosts */
|
|
46
|
+
"ui/resourceUri": string;
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
};
|
|
49
|
+
/** Index signature required by MCP SDK CallToolResult */
|
|
50
|
+
[key: string]: unknown;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=mcp-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-types.d.ts","sourceRoot":"","sources":["../../../src/types/mcp-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EACL,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,YAAY,EACb,MAAM,yBAAyB,CAAC;AAEjC,uDAAuD;AACvD,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,oDAAoD;AACpD,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE;QACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,kEAAkE;AAClE,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC;IAChC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,aAAa,EAAE,aAAa,CAAC;IAC7B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,2EAA2E;IAC3E,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,gEAAgE;AAChE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,iBAAiB,EAAE,uBAAuB,CAAC;IAC3C,KAAK,EAAE;QACL,EAAE,EAAE;YAAE,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5B,iCAAiC;QACjC,gBAAgB,EAAE,MAAM,CAAC;QACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,yDAAyD;IACzD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@brander/mcp-tools",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "BranderUX MCP Tools — Add branded interactive UI to any MCP server",
|
|
6
|
+
"main": "dist/server/lib-entry.js",
|
|
7
|
+
"types": "dist/server/lib-entry.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/server/lib-entry.js",
|
|
11
|
+
"types": "./dist/server/lib-entry.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./standalone": {
|
|
14
|
+
"import": "./dist/server/index.js",
|
|
15
|
+
"types": "./dist/server/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"brander-mcp": "./dist/server/index.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build:app": "vite build",
|
|
27
|
+
"build:server": "tsc -p tsconfig.json",
|
|
28
|
+
"build": "npm run build:app && npm run build:server",
|
|
29
|
+
"dev": "tsx --watch src/index.ts",
|
|
30
|
+
"clean": "rm -rf dist",
|
|
31
|
+
"rebuild": "npm run clean && npm run build"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@modelcontextprotocol/ext-apps": "^1.1.0",
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
36
|
+
"zod": "^4.3.6"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@brander/elements": "file:../brander-elements",
|
|
40
|
+
"@emotion/react": "^11.11.0",
|
|
41
|
+
"@emotion/styled": "^11.11.0",
|
|
42
|
+
"@mui/material": "^6.0.0",
|
|
43
|
+
"@types/node": "^20.0.0",
|
|
44
|
+
"@types/react": "^18.2.0",
|
|
45
|
+
"@types/react-dom": "^18.2.0",
|
|
46
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
47
|
+
"lucide-react": "^0.453.0",
|
|
48
|
+
"react": "^18.3.0",
|
|
49
|
+
"react-dom": "^18.3.0",
|
|
50
|
+
"recharts": "^2.12.0",
|
|
51
|
+
"tsx": "^4.0.0",
|
|
52
|
+
"typescript": "^5.5.4",
|
|
53
|
+
"vite": "^6.0.0",
|
|
54
|
+
"vite-plugin-singlefile": "^2.0.0"
|
|
55
|
+
}
|
|
56
|
+
}
|