|
| 1 | +# Pluggable Widgets MCP Server - AI Agent Guide |
| 2 | + |
| 3 | +This document provides context for AI development assistants working on the MCP (Model Context Protocol) server for Mendix pluggable widgets. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +This package implements an MCP server that enables AI assistants to scaffold and manage Mendix pluggable widgets programmatically. It supports both HTTP and STDIO transports for flexible integration with various MCP clients. |
| 8 | + |
| 9 | +### Key Characteristics |
| 10 | + |
| 11 | +- **MCP SDK**: Built on `@modelcontextprotocol/sdk` for standardized AI tool integration |
| 12 | +- **Dual Transport**: HTTP (Express) for web clients, STDIO for CLI clients (Claude Desktop, etc.) |
| 13 | +- **TypeScript**: Fully typed with Zod schemas for runtime validation |
| 14 | +- **Widget Generator**: Wraps `@mendix/generator-widget` via PTY for interactive scaffolding |
| 15 | + |
| 16 | +## Project Structure |
| 17 | + |
| 18 | +``` |
| 19 | +src/ |
| 20 | +├── index.ts # Entry point - transport mode selection |
| 21 | +├── config.ts # Server configuration and constants |
| 22 | +├── server/ |
| 23 | +│ ├── server.ts # MCP server factory and tool registration |
| 24 | +│ ├── http.ts # HTTP transport setup (Express) |
| 25 | +│ ├── stdio.ts # STDIO transport setup |
| 26 | +│ ├── routes.ts # Express route handlers |
| 27 | +│ └── session.ts # HTTP session management |
| 28 | +└── tools/ |
| 29 | + ├── index.ts # Tool aggregation |
| 30 | + ├── types.ts # MCP tool type definitions |
| 31 | + ├── scaffolding.tools.ts # Widget creation tool |
| 32 | + └── utils/ |
| 33 | + ├── generator.ts # Widget generator PTY wrapper |
| 34 | + ├── progress-tracker.ts # Progress/logging helper |
| 35 | + ├── notifications.ts # MCP notification utilities |
| 36 | + └── response.ts # Tool response helpers |
| 37 | +``` |
| 38 | + |
| 39 | +## Architecture |
| 40 | + |
| 41 | +### Transport Layer |
| 42 | + |
| 43 | +The server supports two transport modes selected via CLI argument: |
| 44 | + |
| 45 | +- **HTTP** (default): Multi-session Express server on port 3100 |
| 46 | +- **STDIO**: Single-session stdin/stdout for CLI integration |
| 47 | + |
| 48 | +### Tool Registration |
| 49 | + |
| 50 | +Tools are defined using the `ToolDefinition<T>` interface: |
| 51 | + |
| 52 | +```typescript |
| 53 | +interface ToolDefinition<T> { |
| 54 | + name: string; // Tool identifier |
| 55 | + title: string; // Human-readable name |
| 56 | + description: string; // LLM-facing description |
| 57 | + inputSchema: ZodType<T>; // Zod schema for validation |
| 58 | + handler: ToolHandler<T>; // Async handler function |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +New tools should be: |
| 63 | + |
| 64 | +1. Created in `src/tools/` with a `*.tools.ts` suffix |
| 65 | +2. Export a `get*Tools()` function returning `ToolDefinition[]` |
| 66 | +3. Registered in `src/tools/index.ts` |
| 67 | + |
| 68 | +### Widget Generator Integration |
| 69 | + |
| 70 | +The `create-widget` tool uses `node-pty` to interact with the Mendix widget generator CLI. Key implementation details: |
| 71 | + |
| 72 | +- **PTY Simulation**: Required because the generator uses interactive prompts |
| 73 | +- **Prompt Detection**: Matches expected prompts in terminal output |
| 74 | +- **Answer Automation**: Sends pre-configured answers based on user input |
| 75 | +- **Progress Tracking**: Reports progress via MCP notifications |
| 76 | + |
| 77 | +## Development Commands |
| 78 | + |
| 79 | +```bash |
| 80 | +pnpm dev # Development mode with hot reload (tsx watch) |
| 81 | +pnpm build # TypeScript compilation + path alias resolution |
| 82 | +pnpm start # Build and run (HTTP mode) |
| 83 | +pnpm start:stdio # Build and run (STDIO mode) |
| 84 | +pnpm lint # ESLint + Prettier check |
| 85 | +``` |
| 86 | + |
| 87 | +## Adding New Tools |
| 88 | + |
| 89 | +1. **Create tool file**: `src/tools/my-feature.tools.ts` |
| 90 | + |
| 91 | +```typescript |
| 92 | +import { z } from "zod"; |
| 93 | +import type { ToolDefinition, ToolResponse } from "@/tools/types"; |
| 94 | +import { createToolResponse, createErrorResponse } from "@/tools/utils/response"; |
| 95 | + |
| 96 | +const mySchema = z.object({ |
| 97 | + param: z.string().describe("Parameter description for LLM") |
| 98 | +}); |
| 99 | + |
| 100 | +type MyInput = z.infer<typeof mySchema>; |
| 101 | + |
| 102 | +export function getMyTools(): ToolDefinition<MyInput>[] { |
| 103 | + return [ |
| 104 | + { |
| 105 | + name: "my-tool", |
| 106 | + title: "My Tool", |
| 107 | + description: "What this tool does (shown to LLM)", |
| 108 | + inputSchema: mySchema, |
| 109 | + handler: async (args, context) => { |
| 110 | + // Implementation |
| 111 | + return createToolResponse("Success message"); |
| 112 | + } |
| 113 | + } |
| 114 | + ]; |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +2. **Register in index**: Update `src/tools/index.ts` |
| 119 | + |
| 120 | +```typescript |
| 121 | +import { getMyTools } from "./my-feature.tools"; |
| 122 | + |
| 123 | +export function getAllTools(): AnyToolDefinition[] { |
| 124 | + return [ |
| 125 | + ...getScaffoldingTools(), |
| 126 | + ...getMyTools() // Add here |
| 127 | + ]; |
| 128 | +} |
| 129 | +``` |
| 130 | + |
| 131 | +## Code Conventions |
| 132 | + |
| 133 | +### Imports |
| 134 | + |
| 135 | +- Use `@/` path alias for absolute imports from `src/` |
| 136 | +- Prefer specific file imports over barrel exports when dealing with circular dependencies |
| 137 | +- Group imports: node builtins → external packages → internal modules |
| 138 | + |
| 139 | +### Error Handling |
| 140 | + |
| 141 | +- Use `createErrorResponse()` for user-facing errors |
| 142 | +- Log to `console.error` (not stdout) in STDIO mode |
| 143 | +- Use `ProgressTracker` for long-running operations |
| 144 | + |
| 145 | +### Type Safety |
| 146 | + |
| 147 | +- All tool inputs must have Zod schemas |
| 148 | +- Use `ToolContext` for MCP-provided context (notifications, progress) |
| 149 | +- Avoid `any` except in `AnyToolDefinition` (required for heterogeneous tool arrays) |
| 150 | + |
| 151 | +## Testing |
| 152 | + |
| 153 | +Use MCP Inspector for interactive testing: |
| 154 | + |
| 155 | +```bash |
| 156 | +# STDIO mode |
| 157 | +npx @modelcontextprotocol/inspector node dist/index.js stdio |
| 158 | + |
| 159 | +# HTTP mode |
| 160 | +pnpm start |
| 161 | +npx @modelcontextprotocol/inspector |
| 162 | +# Connect to http://localhost:3100/mcp |
| 163 | +``` |
| 164 | + |
| 165 | +## Key Files Reference |
| 166 | + |
| 167 | +| File | Purpose | |
| 168 | +| -------------------------- | -------------------------------------- | |
| 169 | +| `config.ts` | All constants (ports, timeouts, paths) | |
| 170 | +| `tools/types.ts` | MCP tool type definitions | |
| 171 | +| `tools/utils/generator.ts` | Widget generator prompts and defaults | |
| 172 | +| `server/session.ts` | HTTP session lifecycle management | |
| 173 | + |
| 174 | +## Common Patterns |
| 175 | + |
| 176 | +### Progress Notifications |
| 177 | + |
| 178 | +```typescript |
| 179 | +const tracker = new ProgressTracker({ |
| 180 | + context, |
| 181 | + logger: "my-tool", |
| 182 | + totalSteps: 5 |
| 183 | +}); |
| 184 | + |
| 185 | +tracker.start("initializing"); |
| 186 | +await tracker.progress(25, "Step 1 complete"); |
| 187 | +await tracker.info("Detailed log message", { key: "value" }); |
| 188 | +tracker.stop(); |
| 189 | +``` |
| 190 | + |
| 191 | +### Long-Running Operations |
| 192 | + |
| 193 | +- Use `ProgressTracker` for heartbeat and stuck detection |
| 194 | +- Set appropriate timeouts (see `SCAFFOLD_TIMEOUT_MS`) |
| 195 | +- Call `tracker.markComplete()` before expected long waits (e.g., npm install) |
| 196 | + |
| 197 | +## Roadmap Context |
| 198 | + |
| 199 | +Current focus is widget scaffolding. Planned additions: |
| 200 | + |
| 201 | +- Widget property editing |
| 202 | +- XML configuration management |
| 203 | +- Build and deployment automation |
| 204 | + |
| 205 | +When adding features, maintain the existing patterns for tool registration, progress tracking, and transport-agnostic design. |
0 commit comments