Website · Changelog · npm · GitHub
A stdio Model Context Protocol server that bridges coding agents (OpenCode, Claude Code, Cursor, Zed) to a running Open Design daemon — so you can list projects, fetch artifacts, and (in upcoming releases) generate full design artifacts via BYOK from inside your editor.
v0.17.0 — 13 MCP tools live. The server activates the full Open Design feature surface: list/inspect projects, save and lint artifacts, persist files inside projects, format Turn 3 prompts, and generate designs via BYOK with the full upstream system-prompt fidelity. See open OpenSpec changes for in-flight work and closed changes for historical decisions.
| Tool | Verb | Env vars required | Description |
|---|---|---|---|
od_list_projects |
read | OD_DAEMON_URL |
List all projects from the OD daemon. Returns {projects: [{id, name, kind?, status?}, …]}. |
od_get_project |
read | OD_DAEMON_URL |
Fetch a project + its artifact files. Merges GET /api/projects/:id and GET /api/projects/:id/files. Now also surfaces customInstructions, fidelity, skillId, designSystemId, createdAt, updatedAt when set on the project (#56). |
od_create_project |
write | OD_DAEMON_URL |
Create a new project. Requires a client-supplied id matching /^[A-Za-z0-9._-]{1,128}$/ plus name; optional kind, fidelity, customInstructions, skillId, designSystemId, pendingPrompt. Returns the project details and an auto-seeded conversation ID. customInstructions is also mirrored to metadata.customInstructions for daemon compat (#43). |
od_update_project |
write | OD_DAEMON_URL |
Update a project's name, custom instructions, or metadata. At least one mutable field is required. customInstructions is also mirrored to metadata.customInstructions for daemon compat (#43). |
od_delete_project |
write | OD_DAEMON_URL |
PERMANENTLY delete a project (database row + on-disk directory). Cannot be undone. |
od_save_artifact |
write | OD_DAEMON_URL |
Persist an HTML artifact to the daemon's global artifact store at /app/.od/artifacts/<timestamp>-<identifier>/index.html. Args: identifier (URL-safe slug, /^[a-z0-9-]+$/, 3–64 chars), title (human-readable, 1–200 chars), html (full document). NOT project-scoped — saved artifacts do not appear in od_get_project.files. Returns the saved path + URL. |
od_save_project_file |
write | OD_DAEMON_URL |
Persist a file inside a project so it appears in od_get_project.files[] and renders in the daemon UI. Wraps POST /api/projects/:id/files. Args: projectId (1–128 chars), name (file name, no path separators, 1–255 chars), content (string, max ~5 MB). Unlike od_save_artifact (global store), this is project-scoped — use it when you want the file to show up under the project's viewer. |
od_extract_design_system |
read | none | Parse a design-system.html artifact and return the JSON manifest + the three CSS blocks (od-tokens, od-components, od-layout). Pure function — no network calls, no env vars. Part of the design-system-first workflow. |
od_generate_design_system |
generate (streaming) | OD_DAEMON_URL + BYOK_BASE_URL + BYOK_API_KEY + BYOK_MODEL |
Generate a design system artifact (design-system.html) via BYOK. Produces tokens, components, and layout CSS plus a JSON manifest and human-reviewable gallery. Post-generation validation ensures the output has all required marker slots. |
od_update_design_system |
write | OD_DAEMON_URL (delta mode: none; semantic mode: + BYOK vars) |
Update an existing design system. Two modes: semantic (BYOK-driven natural-language patch) and delta (deterministic JSON merge, no network). Bumps data-od-version on every successful update. |
od_lint_artifact |
validate | OD_DAEMON_URL |
Lint a raw HTML document. Takes { html } only — inline content, not a project/slug reference. Returns findings + agent message. New optional arg designSystemHtml enables design-system-specific checks (DS001–DS005). |
od_compose_brief |
format | none | Format a Turn 3 prompt for od_generate_design. Combines form answers, brand spec, and page brief into a string upstream Open Design recognizes. Pure function — no network, no env vars. New optional arg designSystemSummary for including design system context in the Turn 3 prompt. |
od_generate_design |
generate (streaming) | OD_DAEMON_URL + BYOK_BASE_URL + BYOK_API_KEY + BYOK_MODEL (BYOK_PROVIDER optional, defaults to openai) |
Generate a design via the BYOK pipeline. Composes the upstream Open Design system prompt and proxies through the OD daemon's /api/proxy/<provider>/stream. Returns the accumulated text. Reads customInstructions from metadata.customInstructions first, then top-level field (#43). Accepts maxTokens (default 64000) to control completion length — see #36. When projectId resolves a project with designSystemId, auto-injects the design system contract. New optional arg designSystemMode (strict/advisory/off). |
Only od_generate_design and od_generate_design_system require the BYOK vars. The other 11 tools work with just OD_DAEMON_URL (or no env vars in the case of od_compose_brief and od_extract_design_system).
od_save_project_file vs od_save_artifact: These two write tools serve different storage scopes. od_save_artifact writes to the daemon's global artifact store — useful for shareable URLs that aren't tied to a project. od_save_project_file writes inside a project's directory so the file appears in od_get_project.files[] and renders in the daemon's project UI. Use od_save_project_file when you want the generated design to live inside the project; use od_save_artifact for standalone, project-unaware artifacts.
When you invoke od_generate_design with a PRD-style prompt, the MCP server composes a ~20–50 KB system prompt locally (vendored composeSystemPrompt injects designer charter + kind-specific rules), then POSTs to the OD daemon's /api/proxy/<provider>/stream endpoint. The daemon is a thin pass-through proxy — it forwards to your BYOK provider, transcodes the upstream stream into its own SSE format, and streams tokens back. The MCP server accumulates deltas and emits notifications/progress every 25 tokens (only when the client supplied a progressToken, per MCP spec).
Typical timing: ~10 seconds for small sections (single hero, paragraph rewrite); 1–5 minutes for full pages; up to 10 minutes for complex multi-section designs. Set maxTokens (default 64000) to control completion length — full pages typically need 30000+ tokens; the daemon's silent default was 8192 (#36). Server timeout defaults to 10 minutes (OD_GENERATE_TIMEOUT_MS, configurable). On abort or timeout mid-stream, accumulated tokens are returned as partial HTML with a trailing <!-- Generation timed out... --> comment marker and isError: true — pair with od_save_artifact to checkpoint partial progress.
Full reference with file:line citations: docs/architecture/generate-design-flow.md.
sequenceDiagram
autonumber
participant E as Editor
participant M as open-design-mcp
participant D as OD daemon
participant P as BYOK proxy
E->>M: tools/call od_generate_design
M->>M: composeSystemPrompt + lazy BYOK load
M->>D: POST /api/proxy/openai/stream
D->>P: POST /chat/completions stream true
loop SSE tokens
P-->>D: delta
D-->>M: event delta
end
M-->>E: tool result HTML
For multi-page projects, generate a design system before any individual pages to ensure visual consistency:
- Generate a design system:
od_generate_design_systemwith your brand brief. - Save it inside the project:
od_save_project_filewithname: "design-system.html". - Link it:
od_update_projectwithdesignSystemId: "design-system.html". - Generate pages: Every subsequent
od_generate_designcall with thatprojectIdauto-injects the design system contract (strict mode by default).
The designSystemMode argument on od_generate_design controls enforcement:
strict(default when linked) — pages MUST use documented tokens and components; violations caught byod_lint_artifact.advisory— prefer documented tokens; deviations should be justified.off— no injection, equivalent to pre-v0.17 behavior.
Use od_lint_artifact with designSystemHtml to verify pages conform (findings DS001–DS005). Use od_update_design_system to evolve the system over time without regenerating all pages.
This workflow is fully opt-in — projects without a linked design system behave exactly as before.
Known limitation in v0.17.0: Step 4 (auto-injection via
designSystemIdlinkage) is currently a runtime no-op. The daemon's files-list endpoint returns metadata only — not file content — sood_generate_designcannot yet fetch and inject the linked design system. Until v0.18 adds a content endpoint, enforce design system adherence viaod_lint_artifact+designSystemHtml(post-generation static checks DS001–DS005) and viaod_compose_brief+designSystemSummary(Turn 3 prompt enrichment). Both paths are fully functional. See CHANGELOG.md v0.17.0 "Known Limitations".
Add the following entry to your MCP client config (OpenCode / Claude Code / Cursor / Zed):
The server boots successfully with only OD_DAEMON_URL set — the BYOK vars are validated lazily when od_generate_design is invoked. This lets users explore via od_list_projects / od_get_project before wiring an AI provider.
The right value depends on where the Open Design daemon is running relative to the MCP server (which itself runs wherever your coding agent spawns it):
| Your setup | OD_DAEMON_URL |
|---|---|
MCP server + OD daemon both run as containers on a shared Docker network (e.g. via ai-sandbox-wrapper with --network ai-sandbox) — most common |
http://ai-open-design:7456 |
MCP server runs inside a Docker container, OD daemon runs natively on the host (exposed on host port 7456) |
http://host.docker.internal:7456 (macOS / Windows Docker Desktop)http://172.17.0.1:7456 (Linux bridge gateway) |
| MCP server and OD daemon both run natively on the host (no Docker) | http://localhost:7456 |
OD container started via ai-run open-design start --expose --port N to host port N |
http://host.docker.internal:N (from inside another container)http://localhost:N (from host) |
Quick diagnostic (run from the environment where the MCP server will spawn):
curl -fsS http://ai-open-design:7456/ && echo " OK" # shared docker network
curl -fsS http://host.docker.internal:7456/ && echo " OK" # host gateway
curl -fsS http://localhost:7456/ && echo " OK" # nativeWhichever one returns 200 is your correct OD_DAEMON_URL.
| Variable | Purpose | Required by | Default |
|---|---|---|---|
OD_DAEMON_URL |
Open Design daemon base URL — see Choosing OD_DAEMON_URL above |
all tools (validated eagerly at startup) | — |
OD_API_TOKEN |
Bearer token the OD daemon enforces when bound to non-loopback | optional | "" (no auth header sent) |
OD_AUTH_MODE |
Auth mode: none, bearer, or basic. Auto-derived if unset (token set → bearer; basic creds set → basic; neither → none) |
optional | inferred |
OD_BASIC_USER |
HTTP Basic Auth username | OD_AUTH_MODE=basic |
— |
OD_BASIC_PASS |
HTTP Basic Auth password | OD_AUTH_MODE=basic |
— |
OD_GENERATE_TIMEOUT_MS |
Server-side timeout for od_generate_design, in milliseconds. Raised from the previous 120s after #33 confirmed full-page generations legitimately exceed it. |
optional | 600000 (10 min) |
BYOK_BASE_URL |
OpenAI-compatible AI provider base URL | od_generate_design only (validated lazily) |
— |
BYOK_API_KEY |
Provider API key forwarded via OD's /api/proxy/*/stream |
od_generate_design only |
— |
BYOK_MODEL |
Model id (e.g. open-design, claude-sonnet-4-6) |
od_generate_design only |
— |
BYOK_PROVIDER |
One of openai / anthropic / azure / google / ollama |
optional | openai |
The server fails fast with a clear stderr message if OD_DAEMON_URL is missing or invalid. BYOK vars are checked only when od_generate_design is called — a missing var yields a friendly tool-level error (isError: true, text "BYOK not configured: missing ..."), never a crash.
If the OD daemon is behind a reverse proxy with HTTP Basic Auth (e.g. the publicly-hosted instance at https://od.thnkandgrow.com/), set OD_AUTH_MODE=basic with the matching credentials:
{
"mcp": {
"open-design": {
"command": "npx",
"args": ["-y", "open-design-mcp"],
"env": {
"OD_DAEMON_URL": "https://od.thnkandgrow.com/",
"OD_AUTH_MODE": "basic",
"OD_BASIC_USER": "<your-username>",
"OD_BASIC_PASS": "<your-password>"
}
}
}
}The server emits Authorization: Basic <base64(user:pass)> on every request to the OD daemon. Embedded credentials in the URL (https://user:pass@host/) are rejected at startup — use the env vars instead.
nvm use # picks Node 20 per .nvmrc
npm install
npm run lint
npm run typecheck
npm test
npm run build
npm run test:integration # spawns dist/src/server.js, mocks the OD daemon, exercises all 8 toolsThe engineering harness (docs/HARNESS.md) requires every feature, fix, or refactor to go through an OpenSpec proposal → deep-design → specs → implement → validate → review → PR → archive cycle. See docs/stories/ for in-flight stories.
This project vendors a subset of source from nexu-io/open-design (Apache License 2.0) so we can compose the same systemPrompt the Open Design web UI builds for its BYOK chat turns — without depending on the upstream's private @open-design/contracts package.
| Path | Upstream | License | Notes |
|---|---|---|---|
vendor/od-contracts/ |
nexu-io/open-design packages/contracts/src/ |
Apache-2.0 | 13 files (7 runtime + 6 type-only). Pinned commit + re-sync instructions in vendor/od-contracts/VENDORED_FROM.md. Sync script at scripts/vendor-sync.sh. |
License compliance:
- A copy of the upstream LICENSE travels in
vendor/od-contracts/LICENSE(§4(a)). - Modifications to vendored files (if any) carry a
MODIFICATIONheader per Apache 2.0 §4(b); seevendor/od-contracts/VENDORED_FROM.mdModifications section for the running log. - Original upstream copyright notices in each vendored file are retained verbatim (§4(c)).
- Attribution is in
vendor/od-contracts/NOTICEand referenced from the top-levelNOTICE(§4(d)).
All vendored code is redistributed under the same Apache License 2.0.
Apache License 2.0. See LICENSE for the full text and NOTICE for attribution.
Copyright (c) 2026 kokorolx kokoro.lehoang@gmail.com.
{ "mcp": { "open-design": { "command": "npx", "args": ["-y", "open-design-mcp"], "env": { // Pick the line that matches your deployment (see "Choosing OD_DAEMON_URL" below) "OD_DAEMON_URL": "http://ai-open-design:7456", "OD_API_TOKEN": "", // optional; bearer token if your daemon requires it // BYOK vars — only needed if you call od_generate_design "BYOK_BASE_URL": "https://your-ai-proxy.example.com/v1", "BYOK_API_KEY": "<provider-api-key>", "BYOK_MODEL": "open-design", "BYOK_PROVIDER": "openai" // optional; one of openai/anthropic/azure/google/ollama } } } }