brand_generate_designmd
Synthesizes multi-page brand evidence into a single DESIGN.md design brief and design-synthesis.json with structured design tokens.
Instructions
Generate DESIGN.md (portable agent-facing design brief) and design-synthesis.json (structured radius, shadow, spacing, layout, motion, component, and personality signals) from the current brand system. Reads extraction-evidence.json when available for grounded visual signals; falls back to core-identity.yaml and tokens.json after manual edits. Use after brand_extract_site or brand_extract_visual to synthesize multi-page evidence into a single design brief. Use after brand_compile if evidence is unavailable. Returns file paths and synthesis source used. Read-only except for writing the two output files. NOT for extracting brand identity — use brand_extract_web or brand_extract_visual first.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| source | No | Source of truth for synthesis. Default prefers extraction evidence when available, otherwise current-brand. | |
| overwrite | No | If false and DESIGN.md + design-synthesis.json already exist, return the existing artifacts without rewriting. |
Implementation Reference
- Main handler function for brand_generate_designmd. Creates a BrandDir instance, checks existence, then calls generateAndPersistDesignArtifacts to produce DESIGN.md and design-synthesis.json. Returns a structured response with file paths, synthesis source, and extracted design signals.
async function handler(input: Params) { const brandDir = new BrandDir(process.cwd()); if (!(await brandDir.exists())) { return buildResponse({ what_happened: "No .brand/ directory found", next_steps: ["Run brand_start first to create the brand system"], data: { error: ERROR_CODES.NOT_INITIALIZED }, }); } const requestedEvidence = input.source === "evidence"; const hasEvidence = await brandDir.hasExtractionEvidence(); const result = await generateAndPersistDesignArtifacts(brandDir, { source: input.source, overwrite: input.overwrite, }); return buildResponse({ what_happened: input.overwrite === false && result.files_written.length === 0 ? "Loaded existing design synthesis artifacts" : "Generated DESIGN.md and design-synthesis.json from the current brand state", next_steps: [ "Use DESIGN.md as the portable agent-facing design brief", "Use design-synthesis.json when you need structured radius/shadow/layout/personality signals", ...(requestedEvidence && !hasEvidence ? ["Extraction evidence was not available, so the synthesis fell back to current-brand mode."] : []), ], data: { source_requested: input.source ?? "auto", source_used: result.source_used, files_written: result.files_written.length > 0 ? result.files_written : ["design-synthesis.json", "DESIGN.md"], design_synthesis_file: ".brand/design-synthesis.json", design_markdown_file: ".brand/DESIGN.md", evidence_summary: result.synthesis.evidence, personality: result.synthesis.personality, ambiguities: result.synthesis.ambiguities, }, }); } - src/tools/brand-generate-designmd.ts:59-76 (registration)Registration function that exports the tool as 'brand_generate_designmd' on the McpServer with params (source, overwrite), description, and a handler that validates via Zod schema then delegates to the main handler.
export function register(server: McpServer) { server.tool( "brand_generate_designmd", "Generate DESIGN.md (portable agent-facing design brief) and design-synthesis.json (structured radius, shadow, spacing, layout, motion, component, and personality signals) from the current brand system. Reads extraction-evidence.json when available for grounded visual signals; falls back to core-identity.yaml and tokens.json after manual edits. Use after brand_extract_site or brand_extract_visual to synthesize multi-page evidence into a single design brief. Use after brand_compile if evidence is unavailable. Returns file paths and synthesis source used. Read-only except for writing the two output files. NOT for extracting brand identity — use brand_extract_web or brand_extract_visual first.", paramsShape, async (args) => { const parsed = ParamsSchema.safeParse(args); if (!parsed.success) { return buildResponse({ what_happened: `Invalid parameters: ${parsed.error.message}`, next_steps: ["Check the source and overwrite parameters"], data: { error: parsed.error.format() }, }); } return handler(parsed.data); }, ); } - Zod schema for the tool's input: 'source' (enum: evidence|current-brand, optional) and 'overwrite' (boolean, default true). Also defines the Params type.
const paramsShape = { source: z.enum(["evidence", "current-brand"]).optional() .describe("Source of truth for synthesis. Default prefers extraction evidence when available, otherwise current-brand."), overwrite: z.boolean().default(true) .describe("If false and DESIGN.md + design-synthesis.json already exist, return the existing artifacts without rewriting."), }; const ParamsSchema = z.object(paramsShape); type Params = z.infer<typeof ParamsSchema>; - src/lib/design-synthesis.ts:964-1010 (helper)Core helper generateAndPersistDesignArtifacts that orchestrates the synthesis. Reads config, identity, evidence, tokens from BrandDir, calls buildDesignSynthesis and renderDesignMarkdown, then persists the results. Also handles the no-overwrite case by returning existing files.
export async function generateAndPersistDesignArtifacts( brandDir: BrandDir, options: { source?: DesignSynthesisSource; overwrite?: boolean; } = {}, ): Promise<PersistedDesignArtifacts> { const overwrite = options.overwrite ?? true; if (!overwrite && (await brandDir.hasDesignSynthesis()) && (await brandDir.hasDesignMarkdown())) { const synthesis = await brandDir.readDesignSynthesis<DesignSynthesisFile>(); const markdown = await brandDir.readMarkdown("DESIGN.md"); return { source_used: synthesis.source, synthesis, markdown, files_written: [], }; } const config = await brandDir.readConfig(); const identity = await brandDir.readCoreIdentity(); const evidence = await brandDir.hasExtractionEvidence() ? await brandDir.readExtractionEvidence<ExtractionEvidenceFile>() : null; const tokens = await brandDir.hasTokens() ? await brandDir.readTokens() : null; const sourceUsed: DesignSynthesisSource = options.source ?? (evidence ? "evidence" : "current-brand"); const synthesis = buildDesignSynthesis(config, identity, { evidence: sourceUsed === "evidence" ? evidence : null, tokens, source: sourceUsed, }); const markdown = renderDesignMarkdown(synthesis); await brandDir.writeDesignSynthesis(synthesis); await brandDir.writeMarkdown("DESIGN.md", markdown); return { source_used: sourceUsed, synthesis, markdown, files_written: ["design-synthesis.json", "DESIGN.md"], }; } - src/lib/design-synthesis.ts:894-962 (helper)renderDesignMarkdown function that produces the DESIGN.md markdown document from the DesignSynthesisFile, covering visual theme, color palette, typography, component styling, layout, depth/elevation, motion, do/don't rules, and agent prompt guide.
export function renderDesignMarkdown(synthesis: DesignSynthesisFile): string { const visualIntro = synthesis.source === "evidence" ? `This document is grounded in rendered site evidence across ${synthesis.evidence.pages_sampled} representative page(s) and ${synthesis.evidence.screenshots_analyzed} screenshot(s).` : "This document was synthesized from the current brand state because no deeper rendered evidence bundle was available."; return [ "# DESIGN.md", "", `Brand: ${synthesis.brand.client_name}`, visualIntro, "", "## 1. Visual Theme and Atmosphere", "", `${synthesis.brand.client_name} reads as ${synthesis.personality.tone}. The palette skews ${synthesis.colors.mood.temperature} with ${synthesis.colors.mood.contrast} contrast, the corners feel ${synthesis.shape.corner_style}, and the overall elevation language is ${synthesis.depth.elevation_style}. Layout density is ${synthesis.layout.density}, which makes the system feel ${synthesis.personality.positioning}.`, "", "## 2. Color Palette and Roles", "", `Primary brand colors: ${synthesis.colors.brand.length > 0 ? synthesis.colors.brand.map((color) => `\`${color.role}\` ${color.value}`).join(", ") : "none confirmed"}.`, `Semantic support colors: ${synthesis.colors.semantic.length > 0 ? synthesis.colors.semantic.map((color) => `\`${color.role}\` ${color.value}`).join(", ") : "none confirmed"}.`, `Additional palette notes: ${synthesis.colors.additional.length > 0 ? synthesis.colors.additional.map((color) => `\`${color.name}\` ${color.value}`).join(", ") : "no additional token-worthy colors"}.`, "", "## 3. Typography Rules", "", `Font families: ${synthesis.typography.families.length > 0 ? synthesis.typography.families.map((family) => `${family.family} (${family.role}, ${family.character})`).join(", ") : "no strong typography signal detected"}.`, `Representative scale: ${synthesis.typography.scale.length > 0 ? synthesis.typography.scale.map((step) => `\`${step.selector}\` ${step.size}/${step.line_height ?? "auto"} weight ${step.weight}`).join(", ") : "no reliable scale extracted"}.`, "", "## 4. Component Styling", "", `Buttons: ${synthesis.components.button.count > 0 ? `dominant fill ${synthesis.components.button.dominant_fill ?? "none"}, text ${synthesis.components.button.dominant_text ?? "inherit"}, radius ${synthesis.components.button.dominant_radius ?? "none"}, shadow ${synthesis.components.button.dominant_shadow ?? "none"}.` : "No button instances were observed."}`, `Cards: ${synthesis.components.card.count > 0 ? `dominant fill ${synthesis.components.card.dominant_fill ?? "none"}, radius ${synthesis.components.card.dominant_radius ?? "none"}, shadow ${synthesis.components.card.dominant_shadow ?? "none"}.` : "No card pattern was observed."}`, `Inputs and nav: inputs use ${synthesis.components.input.dominant_radius ?? "unspecified"} radius; navigation fill resolves to ${synthesis.components.navigation.dominant_fill ?? "unspecified"}.`, `Button variants: ${synthesis.components.variants.buttons.length > 0 ? synthesis.components.variants.buttons.map((variant) => `${variant.variant} (${variant.backgroundColor ?? "transparent"}, radius ${variant.borderRadius ?? "0px"}, padding ${variant.padding ?? "auto"})`).join(", ") : "none detected"}.`, `Input variants: ${synthesis.components.variants.inputs.length > 0 ? synthesis.components.variants.inputs.map((variant) => variant.variant).join(", ") : "none detected"}. Badge variants: ${synthesis.components.variants.badges.length > 0 ? synthesis.components.variants.badges.map((variant) => variant.variant).join(", ") : "none detected"}.`, "", "## 5. Layout Principles", "", `Grid feel: ${synthesis.layout.grid_feel}.`, `Content width: ${synthesis.layout.content_width ?? "not confidently detected"}.`, `Spacing model: base unit ${synthesis.spacing.base_unit ?? "not confidently detected"} with detected scale ${synthesis.spacing.scale.length > 0 ? synthesis.spacing.scale.map((value) => `${value}px`).join(", ") : "unknown"}, common values ${synthesis.spacing.common_values.length > 0 ? synthesis.spacing.common_values.map((item) => `${item.px}px x${item.count}`).join(", ") : "unknown"}, component spacing ${synthesis.spacing.component_spacing.join(", ") || "unknown"}, and larger section spacing ${synthesis.spacing.section_spacing.join(", ") || "unknown"}.`, "", "## 6. Depth and Elevation", "", `Elevation style: ${synthesis.depth.elevation_style}.`, `Shadow scale: ${formatSignalList(synthesis.depth.shadow_scale)}.`, `Observed shadows: ${synthesis.depth.shadows.length > 0 ? synthesis.depth.shadows.map((shadow) => `${shadow.value} (${shadow.context}, x${shadow.count})`).join(", ") : "none detected"}.`, `Radius scale: ${formatSignalList(synthesis.shape.radius_scale)}.`, `Observed radii: ${synthesis.shape.values.length > 0 ? synthesis.shape.values.map((radius) => `${radius.value} x${radius.count}`).join(", ") : "none detected"}; dominant shape language is ${synthesis.shape.dominant_style}.`, "", "## 7. Motion and Interaction Tone", "", synthesis.motion.tone, `Observed duration tokens: ${formatSignalList(synthesis.motion.duration_tokens)}.`, `Observed easing tokens: ${formatSignalList(synthesis.motion.easing_tokens)}.`, "", "## 8. Do and Do Not Rules", "", ...buildDoRules(synthesis).map((rule) => `- Do: ${rule}`), ...buildDontRules(synthesis).map((rule) => `- Do not: ${rule}`), "", "## 9. Agent Prompt Guide", "", `- Describe the brand as ${synthesis.personality.tone}.`, `- Use ${synthesis.layout.grid_feel} layouts with ${synthesis.layout.density} spacing.`, `- Keep color emphasis centered on ${synthesis.colors.brand[0]?.value ?? "the confirmed primary brand color"} for actions and branded highlights.`, `- Preserve the ${synthesis.shape.corner_style} corner language and ${synthesis.depth.elevation_style} depth treatment.`, synthesis.ambiguities.length > 0 ? `- Ask for confirmation on these unresolved areas before high-stakes output: ${synthesis.ambiguities.join(" ")}` : "- The current synthesis is internally consistent enough to use as an agent-facing design brief.", "", ].join("\n"); }