You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: migrate from barrel imports to focused facades pattern
This commit implements a comprehensive architectural improvement that replaces
the previous barrel import pattern (importing everything from utils/index.ts)
with focused facades that expose specific functionality through dedicated
index.ts files in subdirectories.
Key Changes:
- Created 11 focused facade modules in src/utils/ (execution, logging, responses, validation, axe, plugin-registry, xcodemake, template, version, test, log-capture)
- Migrated 65+ tool files from barrel imports to focused facade imports
- Added ESLint rule to prevent regression to barrel imports
- Updated ARCHITECTURE.md to document the new module organization pattern
Performance Impact:
- Eliminates loading of unused modules, reducing startup time
- Improves module resolution speed for Node.js runtime
- Prevents circular dependency issues that were risks with large barrel files
- Enables better tree-shaking for bundlers
Architecture Benefits:
- Clear dependency graph - imports explicitly show what functionality is needed
- Reduced coupling between unrelated utility components
- Better maintainability and code organization
- Type-safe imports with focused interfaces
- Each tool exports standardized interface (`name`, `description`, `schema`, `handler`)
45
-
- Tools are self-contained with no external dependencies
46
-
- Dynamic vs static mode determines loading behavior
47
-
48
-
4.**Tool Registration**
49
-
- Discovered tools automatically registered with server
41
+
3.**Plugin Discovery (Build-Time)**
42
+
- A build-time script (`build-plugins/plugin-discovery.ts`) scans the `src/mcp/tools/` and `src/mcp/resources/` directories
43
+
- It generates `src/core/generated-plugins.ts` and `src/core/generated-resources.ts` with dynamic import maps
44
+
- This approach improves startup performance by avoiding synchronous file system scans and enables code-splitting
45
+
- Tool code is only loaded when needed, reducing initial memory footprint
46
+
47
+
4.**Plugin & Resource Loading (Runtime)**
48
+
- At runtime, `loadPlugins()` and `loadResources()` use the generated loaders from the previous step
49
+
- In **Static Mode**, all workflow loaders are executed at startup to register all tools
50
+
- In **Dynamic Mode**, only the `discover_tools` tool is registered initially
51
+
- The `enableWorkflows` function in `src/core/dynamic-tools.ts` uses generated loaders to dynamically import and register selected workflow tools on demand
52
+
53
+
5.**Tool Registration**
54
+
- Discovered tools automatically registered with server using pre-generated maps
50
55
- No manual registration or configuration required
51
-
- Environment variables can still control dynamic tool discovery
56
+
- Environment variables control dynamic tool discovery behavior
52
57
53
58
5.**Request Handling**
54
59
- MCP client calls tool → server routes to tool handler
@@ -85,6 +90,66 @@ Tools are self-contained units that export a standardized interface. They don't
85
90
- Zod schemas for runtime validation
86
91
- Generic type constraints ensure compile-time safety
87
92
93
+
## Module Organization and Import Strategy
94
+
95
+
### Focused Facades Pattern
96
+
97
+
XcodeBuildMCP has migrated from a traditional "barrel file" export pattern (`src/utils/index.ts`) to a more structured **focused facades** pattern. Each distinct area of functionality within `src/utils` is exposed through its own `index.ts` file in a dedicated subdirectory.
98
+
99
+
**Example Structure:**
100
+
101
+
```
102
+
src/utils/
103
+
├── execution/
104
+
│ └── index.ts # Facade for CommandExecutor, FileSystemExecutor
105
+
├── logging/
106
+
│ └── index.ts # Facade for the logger
107
+
├── responses/
108
+
│ └── index.ts # Facade for error types and response creators
109
+
├── validation/
110
+
│ └── index.ts # Facade for validation utilities
111
+
├── axe/
112
+
│ └── index.ts # Facade for axe UI automation helpers
113
+
├── plugin-registry/
114
+
│ └── index.ts # Facade for plugin system utilities
115
+
├── xcodemake/
116
+
│ └── index.ts # Facade for xcodemake utilities
117
+
├── template/
118
+
│ └── index.ts # Facade for template management utilities
119
+
├── version/
120
+
│ └── index.ts # Facade for version information
121
+
├── test/
122
+
│ └── index.ts # Facade for test utilities
123
+
├── log-capture/
124
+
│ └── index.ts # Facade for log capture utilities
125
+
└── index.ts # Deprecated barrel file (legacy/external use only)
126
+
```
127
+
128
+
This approach offers several architectural benefits:
129
+
130
+
-**Clear Dependencies**: It makes the dependency graph explicit. Importing from `utils/execution` clearly indicates a dependency on command execution logic
131
+
-**Reduced Coupling**: Modules only import the functionality they need, reducing coupling between unrelated utility components
132
+
-**Prevention of Circular Dependencies**: It's much harder to create circular dependencies, which were a risk with the large barrel file
133
+
-**Improved Tree-Shaking**: Bundlers can more effectively eliminate unused code
134
+
-**Performance**: Eliminates loading of unused modules, reducing startup time and memory usage
135
+
136
+
### ESLint Enforcement
137
+
138
+
To maintain this architecture, an ESLint rule in `eslint.config.js` explicitly forbids importing from the deprecated barrel file within the `src/` directory.
message:'Barrel imports from utils/index.js are prohibited. Use focused facade imports instead (e.g., utils/logging/index.js, utils/execution/index.js).'
147
+
}]
148
+
}],
149
+
```
150
+
151
+
This rule prevents regression to the previous barrel import pattern and ensures all new code follows the focused facade architecture.
152
+
88
153
## Component Details
89
154
90
155
### Entry Points
@@ -116,12 +181,12 @@ MCP server wrapper providing:
116
181
### Tool Discovery System
117
182
118
183
#### `src/core/plugin-registry.ts`
119
-
Automatic plugin loading system:
120
-
-Scans `src/mcp/tools/` directory structure using glob patterns
121
-
-Dynamically imports plugin modules
122
-
- Validates plugin interface compliance
123
-
-Handles both default exports and named exports (for re-exports)
124
-
-Supports workflow group metadata via `index.js` files
184
+
Runtime plugin loading system that leverages build-time generated code:
185
+
-Uses `WORKFLOW_LOADERS` and `WORKFLOW_METADATA` maps from the generated `src/core/generated-plugins.ts` file
186
+
-`loadWorkflowGroups()` iterates through the loaders, dynamically importing each workflow module using `await loader()`
187
+
- Validates that each imported module contains the required `workflow` metadata export
188
+
-Aggregates all tools from the loaded workflows into a single map
189
+
-This system eliminates runtime file system scanning, providing significant startup performance boost
125
190
126
191
#### `src/core/plugin-types.ts`
127
192
Plugin type definitions:
@@ -131,49 +196,74 @@ Plugin type definitions:
131
196
132
197
### Tool Implementation
133
198
134
-
Each plugin (`src/mcp/tools/*/*.js`) follows this standardized pattern:
199
+
Each tool is implemented in TypeScript and follows a standardized pattern that separates the core business logic from the MCP handler boilerplate. This is achieved using the `createTypedTool` factory, which provides compile-time and runtime type safety.
// 4. Export the tool definition for auto-discovery
243
+
exportdefault {
244
+
name: 'some_tool',
245
+
description: 'Tool description for AI agents. Example: some_tool({ requiredParam: "value" })',
246
+
schema: someToolSchema.shape, // Expose shape for MCP SDK
247
+
248
+
// 5. Create the handler using the type-safe factory
249
+
handler: createTypedTool(
250
+
someToolSchema,
251
+
someToolLogic,
252
+
getDefaultCommandExecutor,
253
+
),
171
254
};
172
255
```
173
256
257
+
This pattern ensures that:
258
+
- The `someToolLogic` function is highly testable via dependency injection
259
+
- Zod handles all runtime parameter validation automatically
260
+
- The handler is type-safe, preventing unsafe access to parameters
261
+
- Import paths use focused facades for clear dependency management
262
+
```
263
+
174
264
### MCP Resources System
175
265
176
-
XcodeBuildMCP provides dual interfaces: traditional MCP tools and efficient MCP resources for supported clients. Resources are located in `src/mcp/resources/` and are automatically discovered. For more details on creating resources, see the [Plugin Development Guide](docs/PLUGIN_DEVELOPMENT.md).
266
+
XcodeBuildMCP provides dual interfaces: traditional MCP tools and efficient MCP resources for supported clients. Resources are located in `src/mcp/resources/` and are automatically discovered **at build time**. The build process generates `src/core/generated-resources.ts`, which contains dynamic loaders for each resource, improving startup performance. For more details on creating resources, see the [Plugin Development Guide](docs/PLUGIN_DEVELOPMENT.md).
177
267
178
268
#### Resource Architecture
179
269
@@ -201,7 +291,8 @@ Resources can reuse existing tool logic for consistency:
// Testable resource logic separated from MCP handler
@@ -330,12 +421,12 @@ For detailed guidelines, see the [Testing Guide](docs/TESTING.md).
330
421
331
422
### Test Structure Example
332
423
333
-
Tests inject mock "executors" for external interactions like command-line execution or file system access. This allows for deterministic testing of tool logic without mocking the implementation itself.
424
+
Tests inject mock "executors" for external interactions like command-line execution or file system access. This allows for deterministic testing of tool logic without mocking the implementation itself. The project provides helper functions like `createMockExecutor` and `createMockFileSystemExecutor` in `src/test-utils/mock-executors.ts` to facilitate this pattern.
334
425
335
426
```typescript
336
427
import { describe, it, expect } from'vitest';
337
-
import { toolNameLogic } from'../tool-file.js'; // Import the logic function
- The `build-plugins/plugin-discovery.ts` script is executed
464
+
- It scans `src/mcp/tools/` and `src/mcp/resources/` to find all workflows and resources
465
+
- It generates `src/core/generated-plugins.ts` and `src/core/generated-resources.ts` with dynamic import maps
466
+
- This eliminates runtime file system scanning and enables code-splitting
467
+
468
+
3.**TypeScript Compilation**
469
+
-`tsup` compiles the TypeScript source, including the newly generated files, into JavaScript
370
470
- Compiles TypeScript with tsup
371
471
372
-
2.**Build Configuration** (`tsup.config.ts`)
472
+
4.**Build Configuration** (`tsup.config.ts`)
373
473
- Entry points: `index.ts`, `doctor-cli.ts`
374
474
- Output format: ESM
375
475
- Target: Node 18+
376
476
- Source maps enabled
377
477
378
-
3.**Distribution Structure**
478
+
5.**Distribution Structure**
379
479
```
380
480
build/
381
481
├── index.js # Main server executable
@@ -417,6 +517,9 @@ The guide covers:
417
517
418
518
### Startup Performance
419
519
520
+
-**Build-Time Plugin Discovery**: The server avoids expensive and slow file system scans at startup by using pre-generated loader maps. This is the single most significant performance optimization
521
+
-**Code-Splitting**: In Dynamic Mode, tool code is only loaded into memory when its workflow is enabled, reducing the initial memory footprint and parse time
522
+
-**Focused Facades**: Using targeted imports instead of a large barrel file improves module resolution speed for the Node.js runtime
420
523
-**Lazy Loading**: Tools only initialized when registered
message: 'Barrel imports from utils/index.js are prohibited. Use focused facade imports instead (e.g., utils/logging/index.js, utils/execution/index.js).'
0 commit comments