Add standalone OpenGenerativeUI MCP Server#18
Conversation
- Fully self-contained MCP server package (open-generative-ui-mcp) - Exposes design system, skills, and renderer as MCP capabilities - Includes three MCP tools/resources/prompts for generative UI - Production-ready with Docker, configuration, and documentation - Can be deployed independently without demo app dependency - Complete README with deployment guides and API reference
GeneralJerel
left a comment
There was a problem hiding this comment.
Overall the implementation is clean and well-structured — nice separation of concerns across the 4 source files. A few issues to address before merging, noted inline.
|
|
||
| export function loadSkill(name: string): string { | ||
| return readFileSync(resolve(SKILLS_DIR, `${name}.txt`), "utf-8"); | ||
| } |
There was a problem hiding this comment.
Security: Path traversal vulnerability
name comes from MCP resource URI parameters and is passed directly into resolve(). A crafted name like ../../etc/passwd (with .txt appended) could read files outside the skills directory.
Suggest validating the resolved path stays within SKILLS_DIR:
export function loadSkill(name: string): string {
const resolved = resolve(SKILLS_DIR, `${name}.txt`);
if (!resolved.startsWith(resolve(SKILLS_DIR) + "/")) {
throw new Error(`Invalid skill name: ${name}`);
}
return readFileSync(resolved, "utf-8");
}|
|
||
| # Install dependencies | ||
| RUN pnpm install --prod --frozen-lockfile | ||
|
|
There was a problem hiding this comment.
Bug: --prod flag breaks the build step
--prod skips devDependencies, but tsc (used on line 23 via pnpm build) is listed under devDependencies. This Docker build will fail at the RUN pnpm build step.
Should be:
RUN pnpm install --frozen-lockfileOr split into two stages — full install for build, then prune to prod for the runtime image.
apps/mcp/src/index.ts
Outdated
| const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS?.split(",") || ["*"]; | ||
|
|
||
| const server = createMcpServer(); | ||
| const transport = new WebStandardStreamableHTTPServerTransport(); |
There was a problem hiding this comment.
Potential issue: Single shared transport instance
The WebStandardStreamableHTTPServerTransport is instantiated once and shared across all HTTP requests. MCP's streamable HTTP transport is typically per-session — if two clients connect simultaneously, they may experience session conflicts.
Worth verifying whether the SDK handles concurrent sessions internally, or if you need to create a transport per request (or per session ID).
apps/mcp/.gitignore
Outdated
| .DS_Store | ||
| .turbo/ | ||
| .venv/ | ||
| pnpm-lock.yaml |
There was a problem hiding this comment.
Conflict with Dockerfile: The .gitignore excludes pnpm-lock.yaml, but the Dockerfile (line 11) copies it for --frozen-lockfile install. The Docker build will fail unless the lockfile exists at build time.
Either remove this line (and commit the lockfile) or update the Dockerfile to not rely on --frozen-lockfile.
| @@ -0,0 +1,350 @@ | |||
| // OpenGenerativeUI Design System CSS and Bridge JS | |||
| // Forked from apps/app/src/components/generative-ui/widget-renderer.tsx | |||
|
|
|||
There was a problem hiding this comment.
The comment says this is forked from widget-renderer.tsx — that's ~290 lines of duplicated CSS. If the design system evolves, both files need manual sync. Consider extracting the shared CSS to a common file or adding a more visible warning about keeping them in sync.
- Validate resolved path stays within SKILLS_DIR to prevent traversal - Remove --prod from Dockerfile install so tsc is available for build - Remove pnpm-lock.yaml from .gitignore so Dockerfile --frozen-lockfile works - Create per-session transport instances instead of sharing one globally - Add sync warning comment on forked renderer CSS
Claude Desktop uses stdio, not HTTP. Adds src/stdio.ts as a separate entry point alongside the existing HTTP server.
- Add MCP Server section to root README with Claude Desktop and Claude Code setup instructions - Update apps/mcp README with stdio usage for Claude Desktop - Update project structure to include stdio.ts entry point
GeneralJerel
left a comment
There was a problem hiding this comment.
Review Notes
Overall this is clean and well-structured — good separation of concerns, path traversal protection, per-session transports, and dual transport support. Build passes. A few things to consider:
Issues
-
Dockerfile won't build from
apps/mcp/context —Dockerfile:5copiespnpm-lock.yaml, but the lockfile lives at the repo root (this is a workspace package). Runningdocker build -t open-generative-ui-mcp .fromapps/mcp/will fail. Either the build context needs to be the repo root, or the install strategy needs to change. -
Session map has no TTL or size limit —
index.ts:11sessionsMap grows with each new connection and only cleans up on explicit transport close. No eviction for abandoned sessions. Fine for dev, but could leak memory under sustained use. -
LOG_LEVELin.env.exampleis never read — Declared in the example env but not referenced anywhere in the code. Will confuse users trying to configure logging.
Minor observations
SKILLS_DIRfrom env resolves relative to CWD, but the code default resolves relative to__dirname. These behave differently — worth a note in the README or.env.example.unsafe-evalin the CSP (renderer.ts:327) is a deliberate tradeoff for generated widgets, but worth calling out in docs.- The forked CSS in
renderer.ts(~350 lines fromwidget-renderer.tsx) has the sync warning comment which is good — extracting to a shared location would prevent drift long-term.
Verdict
The Dockerfile issue is the only thing that blocks a documented workflow. The rest are non-blocking follow-ups. 👍
Summary
Introduces a fully standalone, independently deployable MCP server that exposes OpenGenerativeUI's design system, skills, and renderer capabilities.
Key Features
open-generative-ui-mcpcan be deployed independentlyassemble_documentwraps HTML with design system CSS and bridge JSskills://listandskills://{name}for browsing skill instructionscreate_widget,create_svg_diagram,create_visualizationFiles Added
apps/mcp/— Complete MCP server packagesrc/— Server implementation (index.ts, server.ts, skills.ts, renderer.ts)skills/— Own copies of skill instruction filesREADME.md— Comprehensive deployment and API documentationDockerfile— Multi-stage production containerUsage
Development
Production (Docker)
docker build -t open-generative-ui-mcp . docker run -p 3100:3100 open-generative-ui-mcpConnect from Claude Code
Add to
.mcp.json:{ "openGenerativeUI": { "url": "http://localhost:3100/mcp" } }Status