Skip to content

Commit 2f0e756

Browse files
committed
Clean up repo
1 parent e24c445 commit 2f0e756

File tree

3 files changed

+25
-28
lines changed

3 files changed

+25
-28
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ The MCP servers in this demo highlight how each tool can light up widgets by com
2323

2424
- `src/` – Source for each widget example.
2525
- `assets/` – Generated HTML, JS, and CSS bundles after running the build step.
26+
- `sample-data/` – Shared catalog data used by the widgets and MCP servers.
2627
- `pizzaz_server_node/` – MCP server implemented with the official TypeScript SDK.
2728
- `pizzaz_server_python/` – Python MCP server that returns the Pizzaz widgets.
2829
- `solar-system_server_python/` – Python MCP server for the 3D solar system widget.
30+
- `ecommerce_server_python/` – Python server that powers the ecommerce carousel sample.
2931
- `build-all.mts` – Vite build orchestrator that produces hashed bundles for every widget entrypoint.
3032

3133
## Prerequisites
@@ -64,23 +66,23 @@ pnpm run dev
6466

6567
## Serve the static assets
6668

67-
If you want to preview the generated bundles without the MCP servers, start the static file server after running a build:
69+
All of the MCP servers expect the bundled HTML, JS, and CSS to be served from the local static file server. After every build, start the server before launching any MCP processes:
6870

6971
```bash
7072
pnpm run serve
7173
```
7274

7375
The assets are exposed at [`http://localhost:4444`](http://localhost:4444) with CORS enabled so that local tooling (including MCP inspectors) can fetch them.
7476

77+
> **Note:** The Python Pizzaz server caches widget HTML with `functools.lru_cache`. If you rebuild or manually edit files in `assets/`, restart the MCP server so it picks up the updated markup.
78+
7579
## Run the MCP servers
7680

7781
The repository ships several demo MCP servers that highlight different widget bundles:
7882

7983
- **Pizzaz (Node & Python)** – pizza-inspired collection of tools and components
8084
- **Solar system (Python)** – 3D solar system viewer
8185

82-
Every tool response includes plain text content, structured JSON, and `_meta.openai/outputTemplate` metadata so the Apps SDK can hydrate the matching widget.
83-
8486
### Pizzaz Node server
8587

8688
```bash
@@ -106,6 +108,7 @@ pip install -r solar-system_server_python/requirements.txt
106108
uvicorn solar-system_server_python.main:app --port 8000
107109
```
108110

111+
Run `pnpm run build` in the repository root before starting the server so it can serve `assets/ecommerce.html`.
109112
You can reuse the same virtual environment for all Python servers—install the dependencies once and run whichever entry point you need.
110113

111114
## Testing in ChatGPT

pizzaz_server_node/src/server.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ function readWidgetHtml(componentName: string): string {
7474
return htmlContents;
7575
}
7676

77-
function widgetMeta(widget: PizzazWidget) {
77+
function widgetDescriptorMeta(widget: PizzazWidget) {
7878
return {
7979
"openai/outputTemplate": widget.templateUri,
8080
"openai/toolInvocation/invoking": widget.invoking,
@@ -84,6 +84,13 @@ function widgetMeta(widget: PizzazWidget) {
8484
} as const;
8585
}
8686

87+
function widgetInvocationMeta(widget: PizzazWidget) {
88+
return {
89+
"openai/toolInvocation/invoking": widget.invoking,
90+
"openai/toolInvocation/invoked": widget.invoked,
91+
} as const;
92+
}
93+
8794
const widgets: PizzazWidget[] = [
8895
{
8996
id: "pizza-map",
@@ -152,7 +159,7 @@ const tools: Tool[] = widgets.map((widget) => ({
152159
description: widget.title,
153160
inputSchema: toolInputSchema,
154161
title: widget.title,
155-
_meta: widgetMeta(widget),
162+
_meta: widgetDescriptorMeta(widget),
156163
// To disable the approval prompt for the widgets
157164
annotations: {
158165
destructiveHint: false,
@@ -166,15 +173,15 @@ const resources: Resource[] = widgets.map((widget) => ({
166173
name: widget.title,
167174
description: `${widget.title} widget markup`,
168175
mimeType: "text/html+skybridge",
169-
_meta: widgetMeta(widget),
176+
_meta: widgetDescriptorMeta(widget),
170177
}));
171178

172179
const resourceTemplates: ResourceTemplate[] = widgets.map((widget) => ({
173180
uriTemplate: widget.templateUri,
174181
name: widget.title,
175182
description: `${widget.title} widget markup`,
176183
mimeType: "text/html+skybridge",
177-
_meta: widgetMeta(widget),
184+
_meta: widgetDescriptorMeta(widget),
178185
}));
179186

180187
function createPizzazServer(): Server {
@@ -213,7 +220,7 @@ function createPizzazServer(): Server {
213220
uri: widget.templateUri,
214221
mimeType: "text/html+skybridge",
215222
text: widget.html,
216-
_meta: widgetMeta(widget),
223+
_meta: widgetDescriptorMeta(widget),
217224
},
218225
],
219226
};
@@ -255,7 +262,7 @@ function createPizzazServer(): Server {
255262
structuredContent: {
256263
pizzaTopping: args.pizzaTopping,
257264
},
258-
_meta: widgetMeta(widget),
265+
_meta: widgetInvocationMeta(widget),
259266
};
260267
}
261268
);

pizzaz_server_python/main.py

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -146,16 +146,11 @@ def _tool_meta(widget: PizzazWidget) -> Dict[str, Any]:
146146
}
147147

148148

149-
def _embedded_widget_resource(widget: PizzazWidget) -> types.EmbeddedResource:
150-
return types.EmbeddedResource(
151-
type="resource",
152-
resource=types.TextResourceContents(
153-
uri=widget.template_uri,
154-
mimeType=MIME_TYPE,
155-
text=widget.html,
156-
title=widget.title,
157-
),
158-
)
149+
def _tool_invocation_meta(widget: PizzazWidget) -> Dict[str, Any]:
150+
return {
151+
"openai/toolInvocation/invoking": widget.invoking,
152+
"openai/toolInvocation/invoked": widget.invoked,
153+
}
159154

160155

161156
@mcp._mcp_server.list_tools()
@@ -262,15 +257,7 @@ async def _call_tool_request(req: types.CallToolRequest) -> types.ServerResult:
262257
)
263258

264259
topping = payload.pizza_topping
265-
widget_resource = _embedded_widget_resource(widget)
266-
meta: Dict[str, Any] = {
267-
"openai.com/widget": widget_resource.model_dump(mode="json"),
268-
"openai/outputTemplate": widget.template_uri,
269-
"openai/toolInvocation/invoking": widget.invoking,
270-
"openai/toolInvocation/invoked": widget.invoked,
271-
"openai/widgetAccessible": True,
272-
"openai/resultCanProduceWidget": True,
273-
}
260+
meta = _tool_invocation_meta(widget)
274261

275262
return types.ServerResult(
276263
types.CallToolResult(

0 commit comments

Comments
 (0)