πΊοΈ MCP Server Plan
Midwest MCP Server Plan
Overview
An MCP (Model Context Protocol) server that exposes the Midwest Component Library and Design System to AI agents. This enables agents to query component APIs, understand usage patterns, and generate correct component code without needing to read the entire codebase.
Goals
- Component Discovery: Allow agents to discover available components and their capabilities
- API Reference: Provide structured API documentation for each component
- Usage Examples: Surface real-world usage examples from previews/tests
- Design System Info: Expose design tokens, color systems, and patterns
- Code Generation: Assist agents in generating correct component usage
MCP Tools
1. list_components
List all available Midwest components with basic metadata.
Returns:
{ "components": [ { "name": "button_component", "class_name": "Midwest::ButtonComponent", "category": "elements", "description": "Clickable button with variants, sizes, states, and icon support", "deprecated": false }, ... ]}2. get_component_api
Get detailed API information for a specific component.
Parameters:
component(string): Component name (e.g., "buttoncomponent", "cardcomponent")
Returns:
{ "name": "button_component", "class_name": "Midwest::ButtonComponent", "description": "Clickable button with variants, sizes, states, and icon support", "file_path": "app/components/midwest/button_component.rb", "options": [ { "name": "as", "type": "symbol", "default": ":link", "description": "HTML element type", "valid_values": ["link", "button", "submit", "reset", "span"] }, { "name": "size", "type": "symbol", "default": ":default", "valid_values": ["large", "default", "small", "tiny"] }, ... ], "slots": [ { "name": "badge", "renders": "one", "component": "Midwest::BadgeComponent" } ], "public_methods": [ { "name": "element_classes", "returns": "string", "description": "CSS classes for the button element" } ], "themes": ["gray", "red", "orange", "gold", "yellow", "green", "lime", "teal", "blue", "indigo", "violet", "magenta", "pink"], "states": ["error", "warning", "alert", "success", "info"]}3. get_component_examples
Get usage examples from previews and tests.
Parameters:
component(string): Component name
Returns:
{ "component": "button_component", "examples": [ { "source": "preview", "file": "demo/app/previews/elements/button_component_preview.rb", "name": "default", "description": "Minimum code needed to render a button", "code_haml": "= midwest_button 'Click me'", "code_erb": "<%= midwest_button 'Click me' %%%>", "code_html": "<a class=\"midwest-button variant-default size-default\">Click me</a>" }, { "source": "preview", "file": "demo/app/previews/elements/button_component_preview.rb", "name": "playground", "description": "Dynamic preview with all options", "parameters": [ {"name": "label", "type": "text", "default": "Click me"}, {"name": "variant", "type": "select", "choices": ["default", "outline", "ghost"]}, ... ] } ]}4. search_components
Search for components by feature, keyword, or capability.
Parameters:
query(string): Search term (e.g., "form input", "navigation", "icon")category(string, optional): Filter by category (elements, patterns, form)
Returns:
{ "query": "form", "results": [ {"component": "form_input", "relevance": 0.95}, {"component": "form_select", "relevance": 0.90}, {"component": "form_checkbox", "relevance": 0.85} ]}5. get_design_tokens
Get design system tokens (colors, spacing, typography).
Parameters:
type(string, optional): "colors", "spacing", "typography", or "all"
Returns:
{ "colors": { "themes": ["gray", "red", "orange", "gold", "yellow", "green", "lime", "teal", "blue", "indigo", "violet", "magenta", "pink"], "states": { "error": "red", "warning": "orange", "alert": "yellow", "success": "green", "info": "cyan" } }, "spacing": { "padding": ["none", "sm", "default", "lg", "xl"], "sizes": ["xs", "sm", "default", "lg", "xl", "2xl"] }, "icons": { "count": 1000, "source": "Material Design Icons", "available_at": "app/assets/icons/available-icons.yml" }}6. get_helper_methods
Get available helper methods and their signatures.
Returns:
{ "helpers": [ { "name": "midwest_button", "component": "Midwest::ButtonComponent", "signature": "midwest_button(label = nil, path = '/', **options, &block)", "description": "Renders a button component" }, { "name": "midwest_form_with", "signature": "midwest_form_with(builder: Midwest::FormBuilder, **args, &block)", "description": "Form builder with Midwest styling" }, ... ]}7. validate_component_usage
Validate proposed component usage before rendering.
Parameters:
component(string): Component nameusage(object): Proposed options/slots
Returns:
{ "valid": true, "warnings": [], "errors": [], "suggestions": [ "Consider adding a tooltip for better accessibility" ]}8. get_accessibility_info
Get accessibility requirements and patterns for a component.
Parameters:
component(string): Component name
Returns:
{ "component": "dialog_component", "role": "dialog", "required_aria": ["aria-labelledby", "aria-modal"], "keyboard": { "escape": "closes dialog", "tab": "traps focus within dialog", "shift_tab": "moves focus backward within dialog" }, "screen_reader": "Announces dialog title on open, announces on close", "focus_management": "Moves focus to first interactive element on open", "testing_checks": [ "Escape key closes dialog", "Focus is trapped while open", "Focus returns to trigger on close" ]}9. get_stimulus_controllers
Get Stimulus controller information for components that use JavaScript.
Returns:
{ "controllers": [ { "name": "midwest-command-palette", "components": ["command_palette_component"], "values": { "hotkey": "String (default: 'k')", "src": "String (optional)", "items": "Array (optional)", "turboFrameUrl": "String (optional)" }, "actions": ["open", "close", "select"], "targets": ["input", "list", "item"] } ]}10. get_migration_guide
Get migration information for breaking changes between versions.
Parameters:
from_version(string): Source version (e.g., "0.19.0")to_version(string): Target version (e.g., "0.20.0")component(string, optional): Filter to specific component
Returns:
{ "from": "0.19.0", "to": "0.20.0", "breaking_changes": [ { "component": "dropdown_component", "change": "removed 'direction' option", "replacement": "use 'position' option with 'top' or 'bottom' values", "example_before": "midwest_dropdown direction: :down", "example_after": "midwest_dropdown position: :bottom" } ], "deprecated": [ { "component": "button_component", "option": "variant: :fab_mobile", "removed_in": "0.21.0", "use_instead": "variant: :fab with mobile_nav: true" } ]}11. get_component_relationships
Get component composition and relationship information.
Parameters:
component(string): Component name
Returns:
{ "component": "button_component", "composes_with": ["tooltip_component", "confirmation_component", "badge_component"], "composed_by": ["button_group_component", "split_button_component"], "common_patterns": [ "Use inside a form_field_group for proper spacing", "Combine with tooltip for help text", "Add confirmation for destructive actions" ], "alternatives": ["link_component", "split_button_component"], "related_components": ["button_group_component", "dropdown_component"]}12. get_form_builder_info
Get form builder integration information.
Returns:
{ "form_builder": "Midwest::FormBuilder", "methods": [ { "name": "input", "component": "Midwest::Form::InputComponent", "signature": "input(attribute, **options)" }, { "name": "select", "component": "Midwest::Form::SelectComponent", "signature": "select(attribute, choices, **options)" } ], "wrapper": "form_field_group required for proper labeling and validation display"}Component Library Specific Considerations
Extended Component API
The get_component_api tool is extended with component library specific fields:
{ "name": "button_component", "class_name": "Midwest::ButtonComponent", "description": "Clickable button with variants, sizes, states, and icon support", "file_path": "app/components/midwest/button_component.rb", "options": [...], "slots": [...], "public_methods": [...], "themes": ["gray", "red", ...], "states": ["error", "warning", ...], // NEW: Component Library Specific "renders": { "html_element": "a", "default_classes": ["midwest-button", "variant-default", "size-default"], "css_selector": "a.midwest-button", "tag_variants": { "link": "a", "button": "button", "submit": "button[type=submit]", "span": "span" } }, "stimulus": { "controllers": ["midwest-form"], "classes": ["loading"], "targets": [] }, "variants": { "valid_combinations": ["default + any size", "outline + any size"], "invalid_combinations": ["fab + large", "tab-vertical + outline"] }, "form_builder": { "supported": false, "use_instead": "form_submit component" }, "responsive": { "behavior": "static", "breakpoints": [] }, "performance": { "javascript": false, "external_deps": [], "lazy_loading": false }}Extended Examples with Template Variants
The get_component_examples tool returns examples in multiple template languages:
{ "component": "button_component", "examples": [ { "name": "default", "description": "Simple button", "templates": { "haml": "= midwest_button 'Click me'", "erb": "<%= midwest_button 'Click me' %%>", "ruby": "helpers.midwest_button('Click me')", "html_output": "<a class=\"midwest-button variant-default size-default\">Click me</a>" } }, { "name": "with_icon", "description": "Button with icon", "templates": { "haml": "= midwest_button 'Save', icon: :check, variant: :primary", "erb": "<%= midwest_button 'Save', icon: :check, variant: :primary %%>" } } ]}Component Warnings & Gotchas
Each component includes common mistakes and warnings:
{ "component": "dialog_component", "warnings": [ "Don't nest dialogs β browser behavior is undefined", "Always provide an accessible name via aria-labelledby or title slot", "Ensure dialog trigger has popovertarget attribute set" ], "common_mistakes": [ { "mistake": "Using multiple dialogs with same ID", "fix": "Always provide unique id: option for each dialog" }, { "mistake": "Forgetting to close dialog on form submit", "fix": "Add data-action='submit->dialog#close' to form" } ]}Design Token Structure
Design tokens are structured with usage guidance:
{ "colors": { "themes": { "indigo": { "css_var": "--theme-500", "hex": "#7c3aed", "usage": "Primary actions, links, emphasis" } }, "states": { "error": { "color": "red", "css_var": "--error-500", "usage": "Form validation, error messages, destructive actions" } }, "semantic_mapping": { "primary": "indigo", "danger": "red", "warning": "orange", "success": "green" } }, "spacing": { "scale": ["none", "xs", "sm", "default", "lg", "xl", "2xl"], "usage": { "none": "Remove default padding", "sm": "Compact layouts", "default": "Standard spacing", "lg": "More breathing room", "xl": "Section separation" } }}MCP Resources
component_catalog
Static resource providing the full component catalog.
components: button_component: class: Midwest::ButtonComponent category: elements summary: Clickable button with variants tags: [form, action, interactive] related: [button_group, split_button]design_system
Static design system reference.
version: "0.20.1"framework: view_componentdependencies: - rails >= 5.0.0 - view_component >= 3.20.0 - stimulusMCP Prompts
component_generator
Prompt template for generating component usage code.
Input:
- User requirement description
- Target template language (HAML, ERB)
Output:
- Generated component code
- Usage notes
accessibility_review
Prompt for reviewing component accessibility.
Input:
- Component usage code
Output:
- Accessibility concerns
- ARIA attribute suggestions
Implementation Architecture
Phase 1: Core Infrastructure (Bundled in Midwest Gem)
The MCP server is integrated directly into the Midwest gem, ensuring version alignment and simplifying distribution.
midwest/βββ app/β βββ components/midwest/ # Component sources (parsed by MCP)βββ lib/β βββ midwest/β β βββ engine.rbβ β βββ version.rbβ β βββ mcp/ # MCP server integrated hereβ β βββ server.rb # Main MCP server classβ β βββ tools/β β β βββ list_components.rbβ β β βββ get_component_api.rbβ β β βββ get_component_examples.rbβ β β βββ search_components.rbβ β β βββ get_design_tokens.rbβ β β βββ get_helper_methods.rbβ β β βββ validate_component_usage.rbβ β β βββ get_accessibility_info.rbβ β β βββ get_stimulus_controllers.rbβ β β βββ get_migration_guide.rbβ β β βββ get_component_relationships.rbβ β β βββ get_form_builder_info.rbβ β β βββ suggest_components.rb # Context-aware suggestionsβ β β βββ diagnose_component.rb # Debug/error diagnosisβ β β βββ get_responsive_info.rb # Breakpoint patternsβ β βββ resources/β β β βββ component_catalog.rbβ β β βββ design_system.rbβ β β βββ migration_history.rbβ β β βββ responsive_tokens.rb # Breakpoint systemβ β βββ parsers/β β β βββ component_parser.rb # Parse component filesβ β β βββ option_parser.rb # Extract option definitionsβ β β βββ slot_parser.rb # Extract renders_one/manyβ β β βββ preview_parser.rb # Parse preview filesβ β β βββ stimulus_parser.rb # Extract Stimulus controller infoβ β β βββ accessibility_parser.rb # Extract ARIA/keyboard patternsβ β β βββ template_parser.rb # Parse and convert template syntaxβ β β βββ form_builder_parser.rb # Parse FormBuilder integrationβ β β βββ changelog_parser.rb # Parse CHANGELOG for migrationsβ β β βββ asset_parser.rb # Extract JS/CSS dependenciesβ β β βββ validation_parser.rb # Parse validation patternsβ β βββ index/β β β βββ component_index.rb # In-memory component indexβ β β βββ search_index.rb # Search functionalityβ β β βββ relationship_index.rb # Component relationshipsβ β β βββ framework_index.rb # Rails vs Bridgetown supportβ β βββ lookbook/ # Lookbook integrationβ β β βββ extension.rb # Lookbook panel extensionβ β β βββ preview_generator.rb # Auto-generate preview filesβ β β βββ panel_renderer.rb # API panel templateβ β βββ cli/β β β βββ docs_generator.rb # Static docs generationβ β β βββ lookbook_sync.rb # Sync with Lookbookβ β β βββ validate.rb # Validate docs vs codeβ β βββ templates/β β βββ code_generator.rb # Code generation promptsβββ app/β βββ views/β βββ components/β βββ midwest/β βββ lookbook/β βββ _api_panel.erb # Lookbook API panelβββ exe/β βββ midwest_mcp # Executable entry pointβββ demo/β βββ app/previews/ # Usage examples (parsed by MCP)βββ Gemfileβββ midwest.gemspecβββ bin/release # Updated to include MCPPhase 2: Component Parsing
The server locates and parses Midwest files at startup. Since it's bundled in the gem, it uses Gem::Specification to find its own files:
# lib/midwest/mcp/index/component_index.rbmodule Midwest module MCP class ComponentIndex def initialize @gem_spec = Gem::Specification.find_by_name('midwest') @gem_root = @gem_spec.gem_dir end def component_files @component_files ||= Dir[ File.join(@gem_root, 'app', 'components', 'midwest', '**', '*_component.rb') ] end def preview_files @preview_files ||= Dir[ File.join(@gem_root, 'demo', 'app', 'previews', '**', '*_preview.rb') ] end end endendParsing steps:
- Scan component files - Find all
*_component.rbfiles in the gem - Parse each file to extract:
- Class name and inheritance
optiondefinitions (via OptStruct)renders_one/renders_manyslot definitions- Public method signatures
- Constant definitions (valid options, themes, etc.)
- Stimulus controller references (data-controller, data-action, data-target)
- ARIA attributes and accessibility patterns
- Form builder integration markers
- Scan preview files for usage examples
- Extract example names and descriptions
- Parse @param declarations for dynamic previews
- Capture code snippets (convert to multiple template languages)
- Build search index for component discovery
- Analyze relationships - Build graph of component compositions
- Parse Stimulus controllers - Extract controller definitions from JavaScript
- Generate migration data - Track breaking changes from git history or CHANGELOG
Context-Aware Suggestions
The MCP server can accept file context to provide intelligent component suggestions:
Tool: suggest_components
Parameters:
{ "file_path": "app/views/users/_form.html.haml", "surrounding_code": "= midwest_form_with @user do |f|\n = f.input :name", "cursor_position": 42}Returns:
{ "suggestions": [ { "component": "form_input", "helper": "f.input", "reason": "Continues form pattern, matches FormBuilder usage" }, { "component": "form_select", "helper": "f.select", "reason": "Common form input type" }, { "component": "form_field_group", "helper": "f.field_group", "reason": "Wraps related form fields" } ], "context": { "in_form_builder": true, "form_object": "@user", "template_language": "haml" }}Note: In development (running from the repo), the server uses the current working directory instead of the gem path.
Phase 3: Server Protocol
Implement using official MCP Ruby SDK:
require 'mcp/sdk'require_relative 'midwest_mcp/server'server = MidwestMCP::Server.newserver.runPhase 4: Distribution & Release Process
The MCP server is bundled with each Midwest release. When a new version is cut:
# Standard release process (unchanged)$ bin/release 0.21.0# This now also:# 1. Builds the gem with MCP server included# 2. Pushes to RubyGems with MCP server available# 3. Updates version info in MCP server responsesGem Specification:
# midwest.gemspecspec.add_dependency 'mcp-sdk', '~> 1.0' # Lightweight dependencyspec.executables << 'midwest_mcp' # Expose executableInstallation & Usage
For Midwest Consumers
When you install Midwest, the MCP server is automatically available:
# Gemfilegem 'midwest', '~> 0.21.0'Then configure your MCP client (Claude Code, Claude Desktop):
// ~/.claude/settings.json or .clauderc{ "mcpServers": { "midwest": { "command": "midwest_mcp" } }}Or for development/local Midwest checkout:
{ "mcpServers": { "midwest": { "command": "bundle", "args": ["exec", "midwest_mcp"], "cwd": "/path/to/midwest" } }}Standalone Usage (for contributors)
# From the Midwest repo$ bundle exec midwest_mcp# Server starts on stdio, ready for MCP protocol communicationVersion Alignment
The MCP server automatically reports the Midwest version it's serving:
{ "server_info": { "name": "midwest", "version": "0.21.0", "midwest_version": "0.21.0" }}This ensures agents always get documentation matching the installed Midwest version.
Lookbook Integration
The component metadata parsed by the MCP server can power Lookbook documentation directly, creating a single source of truth for component docs.
Architecture
Component Code β MCP Parser β Metadata β Lookbook UI β Preview Panels API Documentation Interactive ParamsRails Engine Integration
A Lookbook extension that displays MCP-parsed metadata:
# lib/midwest/lookbook_extension.rbmodule Midwest module LookbookExtension # Inject API docs panel into Lookbook def self.register Lookbook.addEventListener(:panel, :component) do |panel| panel.add(:midwest_api, "Midwest API", position: 0) do render "midwest/lookbook/api_panel", component: panel.component_name, metadata: Midwest::MCP::ComponentIndex.find(panel.component_name) end end end endendPanel Template
<% app/views/components/midwest/lookbook/_api_panel.erb %%><div class="midwest-api-panel"> <h3><%= @metadata[:class_name] %%></h3> <p><%= @metadata[:description] %%></p> <section> <h4>Options</h4> <table> <% @metadata[:options].each do |opt| %%> <tr> <td><code><%= opt[:name] %%></code></td> <td><%= opt[:type] %%></td> <td><%= opt[:default] %%></td> <td><%= opt[:description] %%></td> </tr> <% end %%> </table> </section> <section> <h4>Slots</h4> <% @metadata[:slots].each do |slot| %%> <div> <code><%= slot[:name] %%></code> (<%= slot[:renders] %%>) <%= slot[:component] %%> </div> <% end %%> </section> <section> <h4>Accessibility</h4> <ul> <% @metadata[:accessibility][:keyboard].each do |key, action| %%> <li><code><%= key %%></code>: <%= action %%></li> <% end %%> </ul> </section> <section> <h4>Stimulus Controllers</h4> <% @metadata[:stimulus][:controllers].each do |controller| %%> <div> <code>data-controller="<%= controller %%>"</code> </div> <% end %%> </section></div>Auto-Generated Preview Files
The MCP server can generate Lookbook preview files from component code:
# Generate preview files from parsed metadata$ bundle exec midwest_mcp lookbook:generate# Creates:# - demo/app/previews/elements/{component}_preview.rb# - demo/app/previews/elements/{component}_preview.html.hamlPreview File Template
# demo/app/previews/elements/button_component_preview.rb (auto-generated)# DO NOT EDIT - Generated by Midwest MCP from component source# To modify, edit app/components/midwest/button_component.rb or add custom previews belowmodule Elements class ButtonComponentPreview < ViewComponent::Preview # @!group Component API # Button component with variants, sizes, and states. # # **Options:** # - `as` (Symbol): HTML element type - default: `:link` # - `size` (Symbol): Size variant - default: `:default` # - `variant` (Symbol): Visual style - default: `:default` # - `state` (Symbol): Semantic state - default: `:default` # - `icon` (Symbol): Optional icon name # - `loading` (Boolean): Show loading state # # **Slots:** # - `badge`: BadgeComponent for notification counts # # **Accessibility:** # - Keyboard: Full keyboard navigation # - ARIA: Proper labels required when icon-only # @!endgroup # @param label text "Button label" # @param size select { choices: [large, default, small, tiny] } # @param variant select { choices: [default, outline, ghost, fab] } # @param state select { choices: [default, error, warning, alert, success, info] } # @param icon select :icon_options # @param loading toggle def playground(label: 'Click me', size: 'default', variant: 'default', state: 'default', icon: nil, loading: false); end # @!group Custom Previews # Add custom previews below this line # These will be preserved on regeneration # --- Custom previews start --- # --- Custom previews end --- endendTwo-Way Sync
Code β Docs:
# Regenerate docs from code$ bundle exec midwest_mcp lookbook:syncDocs β Code (validation):
# Validate docs match code$ bundle exec midwest_mcp lookbook:validateInteractive API Panel in Lookbook
The API panel could include:
- Component Browser - Tree of all components
- Quick Search - Find components by keyword
- Related Components - Shows composition relationships
- Copy Snippets - Copy HAML/ERB code directly
- Live Parameter Editor - See what each option does
- Accessibility Checklist - WCAG compliance notes
- Stimulus Inspector - What controllers are attached
Configuration
# config/initializers/midwest.rbMidwest::Lookbook.configure do |config| config.auto_sync = true # Regenerate on file change config.validate_previews = true # Warn if preview out of sync config.include_source_link = true # Link to component source config.include_examples = true # Show usage examples config.show_accessibility = true # Include a11y panel config.show_stimulus = true # Include Stimulus panelendLookbook Preview URLs via MCP
The MCP server can provide direct links to previews:
{ "component": "button_component", "lookbook": { "url": "/rails/lookbook/inspect/elements/button/playground", "embed_url": "/rails/lookbook/inspect/elements/button/playground?preview=1" }}Consumer Experience
Server starts on stdio, ready for MCP protocol communication
### Version AlignmentThe MCP server automatically reports the Midwest version it's serving:```json{ "server_info": { "name": "midwest", "version": "0.21.0", "midwest_version": "0.21.0" }}This ensures agents always get documentation matching the installed Midwest version.
Consumer Experience
In a Rails Application
# 1. Add Midwest to Gemfilegem 'midwest', '~> 0.21.0'# 2. Bundle install$ bundle install# 3. Configure Claude Code$ cat ~/.claude/settings.json{ "mcpServers": { "midwest": { "command": "bundle", "args": ["exec", "midwest_mcp"], "cwd": "/path/to/your/rails/app" } }}# 4. Start Claude Code in your project$ claude-code# 5. Ask about components!Example Interaction
User: "Add a command palette to search users"
Claude:
- Calls
search_components("command palette") - Calls
get_component_api("command_palette_component") - Calls
get_component_examples("command_palette_component") - Generates:
haml = midwest_command_palette \ placeholder: "Search users...", turbo_frame_url: users_path(format: :turbo_frame), hotkey: "cmd+k"
Multi-Project Setup
For multiple Rails apps using different Midwest versions:
{ "mcpServers": { "midwest-app1": { "command": "bundle", "args": ["exec", "midwest_mcp"], "cwd": "/path/to/app1" }, "midwest-app2": { "command": "bundle", "args": ["exec", "midwest_mcp"], "cwd": "/path/to/app2" } }}Each server reports its Midwest version, so agents use the correct API.
Maintaining Component Metadata
The MCP server needs component-specific metadata that may not be derivable from code alone. Several approaches can be combined:
1. Inline Documentation Comments
# app/components/midwest/dialog_component.rbmodule Midwest class DialogComponent < BaseComponent # @mcp.warning Don't nest dialogs β browser behavior is undefined # @mcp.warning Always provide an accessible name via aria-labelledby or title slot # @mcp.keyboard Escape key closes dialog, focus is trapped # @mcp.aria_role dialog # @mcp.required_aria aria-labelledby, aria-modal endend2. Sidecar YAML Files
# app/components/midwest/dialog_component.mcp.ymlwarnings: - "Don't nest dialogs β browser behavior is undefined" - "Always provide an accessible name via aria-labelledby or title slot"common_mistakes: - mistake: "Using multiple dialogs with same ID" fix: "Always provide unique id: option for each dialog"keyboard: escape: "closes dialog" tab: "moves focus within dialog (trapped)"aria: role: "dialog" required: ["aria-labelledby", "aria-modal"]3. CHANGELOG Driven Migration Data
# CHANGELOG.md (parsed by MCP)---## 0.20.0 (2025-01-15)### Breaking Changes- **dropdown_component**: Removed `direction` option. Use `position` with 'top' or 'bottom' instead.- **button_component**: Renamed `variant: :fab_mobile` to `variant: :fab` with `mobile_nav: true`### Deprecated- **form_input**: `inline` option is deprecated and will be removed in 0.21.0. Use field_group layout instead.4. Automated Git History Analysis
For breaking changes without explicit documentation:
# lib/midwest/mcp/parsers/migration_parser.rbmodule Midwest module MCP class MigrationParser def detect_breaking_changes(from_tag, to_tag) # Compare option definitions between versions # Identify removed options, renamed slots, etc. # Generate migration suggestions end end endend5. Component Relationship Graph
Built from analyzing slot types and imports:
# lib/midwest/mcp/index/relationship_index.rbmodule Midwest module MCP class RelationshipIndex def build_graph components.each do |component| component.slots.each do |slot| add_edge(component.name, slot.component, type: :composes_with) end end # Detect reverse relationships components.each do |component| component.uses.each do |used_component| add_edge(used_component, component.name, type: :composed_by) end end end end endendData Model
Component Schema
{ "$id": "component", "type": "object", "properties": { "name": { "type": "string" }, "class_name": { "type": "string" }, "category": { "type": "string", "enum": ["elements", "patterns", "form"] }, "description": { "type": "string" }, "file_path": { "type": "string" }, "deprecated": { "type": "boolean" }, "options": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "type": { "type": "string" }, "default": { }, "description": { "type": "string" }, "valid_values": { "type": "array", "items": { "type": "string" } } } } }, "slots": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "renders": { "type": "string", "enum": ["one", "many"] }, "component": { "type": "string" } } } } }}Considerations
Performance
- Parse components once at startup, cache in memory
- Use lazy loading for examples (parse on demand)
- Consider incremental reload in development mode
Versioning
- Include Midwest version in all responses
- Support multiple Midwest versions via version parameter
- Deprecation warnings for removed components/options
Security
- Validate all input parameters
- Sanitize code examples before return
- Rate limit search operations
Extensibility
- Plugin system for custom tools
- Support for custom design tokens
- Hook points for preprocessing components
Future Enhancements
- Visual Preview: URL generation for component preview pages
- Migration Guides: Version-to-version migration assistance
- Composition Suggestions: Recommend component combinations
- Theme Builder: Interactive theme customization
- Test Coverage: Expose test coverage per component
- Changelog Integration: Version history and changes
Release Process Integration
Updating bin/release
The existing release script is extended to handle the MCP server:
#!/bin/bashVERSION=$1# Existing steps...git tag -s "v$VERSION" -m "Version $VERSION"git push origin "v$VERSION"gem build midwest.gemspecgem push "midwest-$VERSION.gem"# MCP server is automatically included in the gem build# No separate release needed!Version Bumping
When bumping the version:
# lib/midwest/version.rbmodule Midwest VERSION = '0.21.0'end# lib/midwest/mcp/server.rb automatically uses thisdef server_version Midwest::VERSIONendAdvantages of Bundled Approach
- Automatic Version Alignment - MCP server always matches Midwest version
- Single Dependency - No separate gem to install or maintain
- Simplified Releases - One release process for everything
- Direct Access - Server can parse gem's component files directly
- Zero Config -
midwest_mcpexecutable just works aftergem install midwest - Development Friendliness -
bundle exec midwest_mcpworks in the repo
Additional Component Library Features
Multi-Framework Support
Midwest supports both Rails and Bridgetown β the MCP server exposes framework-specific information:
{ "component": "button_component", "frameworks": { "rails": { "supported": true, "helper": "midwest_button", "integration": "Automatic via Midwest::Engine" }, "bridgetown": { "supported": true, "helper": "midwest_button", "integration": "Requires Midwest::Helpers in site config" } }}Asset & JavaScript Dependencies
Track which components require JavaScript assets:
{ "component": "chart_component", "assets": { "javascript": ["Chart.js"], "css": [], "stimulus_controllers": ["midwest-chart"], "turbo_frames": false, "external_cdn": "https://cdn.jsdelivr.net/npm/chart.js" }, "performance_impact": "high", "lazy_loading_supported": true}Form Validation Patterns
Expose Shopify-style validation integration:
{ "form_validation": { "pattern": "Shopify-style field errors", "error_display": "error state on wrapper + error message below", "integration": { "rails": "works with ActiveModel validations", "custom": "pass errors: hash or use error slot" }, "states": ["error", "warning", "success"], "example": { "valid": "field_group with state: :error", "error_message": "use errors slot or manual message" } }}Responsive Breakpoint System
Based on Tailwind's breakpoints:
{ "responsive": { "breakpoints": { "sm": "640px", "md": "768px", "lg": "1024px", "xl": "1280px", "2xl": "1536px" }, "mobile_first": true, "prefix": "midwest", "components_with_breakpoints": ["mobile_nav", "grid", "table"] }}Component Lifecycle States
Track stability of components:
{ "component": "experimental_feature", "status": { "stability": "alpha", "as_of": "0.21.0", "expected_stable": "0.23.0", "breaking_changes_likely": true, "feedback_requested": true }}Possible statuses: stable, beta, alpha, deprecated, removed
Customization Points
Document what can and cannot be customized:
{ "component": "card_component", "customization": { "allowed": [ "CSS variables via --theme-* overrides", "padding option (none, sm, default, lg, xl)", "class attribute for custom classes" ], "discouraged": [ "Direct CSS targeting of .midwest-card internals", "Modifying slot structure" ], "extension_points": [ "header slot", "footer slot", "section slot" ] }}Error Diagnosis
Help debug component issues:
{ "tool": "diagnose_component", "component": "dialog_component", "symptoms": ["dialog doesn't open on click"], "checks": [ "Verify popovertarget attribute matches dialog id", "Check if another popover is already open", "Ensure dialog_id is unique on page", "Confirm Stimulus controller is mounted" ], "common_fixes": [ "Add popovertarget='{dialog_id}' to trigger button", "Close existing dialogs before opening new one" ]}Animation & Transitions
Document animation behavior:
{ "component": "dialog_component", "animations": { "open": "fade-in + scale", "close": "fade-out + scale", "duration": "200ms", "easing": "ease-out", "customizable": "via CSS variables --dialog-animation-*", "respect_prefers_reduced_motion": true }}i18n Support
Internationalization capabilities:
{ "component": "pagination_component", "i18n": { "supported": true, "keys": ["previous", "next", "page_x_of_y", "per_page"], "rails_integration": "I18n.t('midwest.pagination.*')", "custom_translation": "pass labels: hash option", "rtl_support": true }}Design Token Export
For design tool integration:
# Export design tokens for Figma, Sketch, etc.$ bundle exec midwest_mcp tokens:export --format json --output tokens.json$ bundle exec midwest_mcp tokens:export --format css --output tokens.css$ bundle exec midwest_mcp tokens:export --format scss --output tokens.scssComponent Relationship Graph
Visual representation of component relationships:
{ "graph": { "nodes": ["button", "button_group", "tooltip", "confirmation", "badge"], "edges": [ {"from": "button", "to": "tooltip", "type": "composes_with"}, {"from": "button", "to": "confirmation", "type": "wraps"}, {"from": "button", "to": "badge", "type": "renders_slot"}, {"from": "button_group", "to": "button", "type": "contains_many"} ] }}Turbo Integration
Turbo-specific patterns:
{ "component": "form_component", "turbo": { "supported": true, "patterns": [ "Turbo Frames for pagination", "Turbo Streams for form submission", "Turbo Channels for real-time updates" ], "examples": [ "table with infinite scroll", "form with turbo_stream response" ] }}Dependencies
Midwest Gem Dependencies
# midwest.gemspec - Updated for MCP supportspec.name = 'midwest'spec.version = Midwest::VERSIONspec.authors = ['William A. Nova']spec.email = ['will@unabridgedsoftware.com']spec.homepage = 'https://github.com/unabridgedsoftware/midwest'spec.summary = 'ViewComponents for Midwest Design System'spec.license = 'MIT'# MCP dependency - lightweight, only needed when running the serverspec.add_dependency 'mcp-sdk', '~> 1.0', optional: true# Existing dependenciesspec.add_dependency 'opt_struct'spec.add_dependency 'rails', '>= 5.0.0'spec.add_dependency 'view_component', '>= 3.20.0'# Expose MCP executablespec.executables << 'midwest_mcp'# Include MCP server filesspec.files += Dir['lib/midwest/mcp/**/*.rb']spec.files += Dir['exe/midwest_mcp']Runtime vs Optional
The mcp-sdk dependency is marked optional: true because:
- It's only needed when running the MCP server
- Regular Rails apps using Midwest don't need it
- Keeps the gem lightweight for most consumers
- Users who want MCP support simply install the dependency
# For Rails apps (no MCP needed)gem 'midwest' # Works fine without mcp-sdk# For Claude Code users (needs MCP)gem 'midwest'gem 'mcp-sdk' # Or let bundler install it when running midwest_mcpRelated Files
- Component sources:
app/components/midwest/ - Previews:
demo/app/previews/ - Helper definitions:
app/helpers/midwest/view_helper.rb - Base component:
app/components/midwest/base_component.rb - Icon list:
app/assets/icons/available-icons.yml