mcp-server-gpt-image
Version:
MCP server for OpenAI GPT Image-1 and Responses API with dual-mode support, real-time streaming, intelligent caching, and automatic image optimization
278 lines (218 loc) • 8.49 kB
Markdown
# Architecture Documentation
This document describes the architecture of the MCP Server GPT Image-1 project, focusing on the SOLID principles implementation and clean architecture patterns used throughout the codebase.
## Overview
The project follows clean architecture principles with a focus on:
- **Separation of Concerns**: Each component has a single, well-defined responsibility
- **Dependency Inversion**: High-level modules don't depend on low-level modules
- **Testability**: All business logic is easily testable through dependency injection
- **Extensibility**: New features can be added without modifying existing code
## SOLID Principles Implementation
### 1. Single Responsibility Principle (SRP)
Each class has one reason to change:
```typescript
// ❌ Before: Multiple responsibilities
class ImageGeneration {
generateImage() { /* API calls + caching + optimization */ }
optimizeImage() { /* optimization logic */ }
cacheResult() { /* caching logic */ }
}
// ✅ After: Single responsibilities
class ImageGenerator {
generate() { /* only generation logic */ }
}
class ImageOptimizer {
optimize() { /* only optimization logic */ }
}
class ImageCache {
get() { /* only caching logic */ }
set() { /* only caching logic */ }
}
```
### 2. Open/Closed Principle (OCP)
Classes are open for extension but closed for modification:
```typescript
// Interfaces allow extension without modification
interface IImageGenerator {
generate(input: ImageGenerationInput): Promise<ImageGenerationResult>;
}
// New implementations can be added without changing existing code
class StandardImageGenerator implements IImageGenerator { }
class StreamingImageGenerator implements IImageGenerator { }
```
### 3. Liskov Substitution Principle (LSP)
Derived classes can be substituted for their base classes:
```typescript
// All implementations follow the same contract
const generators: IImageGenerator[] = [
new StandardImageGenerator(),
new StreamingImageGenerator()
];
// Any generator can be used interchangeably
generators.forEach(gen => gen.generate(input));
```
### 4. Interface Segregation Principle (ISP)
Clients shouldn't depend on interfaces they don't use:
```typescript
// Segregated interfaces for different concerns
interface IImageCache {
get(type: string, input: any): Promise<any>;
set(type: string, input: any, data: any): Promise<void>;
}
interface IImageOptimizer {
optimizeBatch(images: string[], input: any): Promise<string[]>;
calculateSizeReduction(original: string, optimized: string): Promise<number>;
}
```
### 5. Dependency Inversion Principle (DIP)
Depend on abstractions, not concretions:
```typescript
// High-level service depends on abstractions
class ImageGenerator {
constructor(
private openaiClient: IOpenAIClient, // interface
private cache: IImageCache, // interface
private optimizer: IImageOptimizer // interface
) {}
}
```
## Project Structure
```
src/
├── interfaces/ # Abstract contracts (DIP)
│ └── image-generation.interface.ts
│
├── services/ # Core business logic (SRP)
│ ├── image-generator.ts
│ ├── streaming-image-generator.ts
│ ├── file-converter.ts
│ └── openai-client-adapter.ts
│
├── adapters/ # Interface implementations (OCP)
│ ├── cache-adapter.ts
│ └── optimizer-adapter.ts
│
├── tools/ # MCP tool endpoints
│ ├── image-generation.ts
│ └── image-generation-streaming.ts
│
├── utils/ # Utility classes
│ ├── cache.ts
│ └── image-optimizer.ts
│
└── transport/ # Communication layer
└── http.ts
```
## Key Components
### 1. Interfaces Layer (`src/interfaces/`)
Defines contracts for all major components:
- `IImageGenerator`: Image generation contract
- `IImageCache`: Caching operations contract
- `IImageOptimizer`: Image optimization contract
- `IOpenAIClient`: OpenAI API contract
- `IFileConverter`: File conversion contract
### 2. Services Layer (`src/services/`)
Contains core business logic implementations:
#### ImageGenerator
- Handles standard image generation
- Orchestrates caching, API calls, and optimization
- Depends only on interfaces
#### StreamingImageGenerator
- Implements streaming image generation
- Emits progress events during generation
- Supports partial image previews
#### FileConverter
- Converts between base64 and File objects
- Extracts base64 from data URLs
- Pure utility service with no external dependencies
#### OpenAIClientAdapter
- Adapts OpenAI SDK to our interface
- Handles API error translation
- Isolates third-party dependencies
### 3. Adapters Layer (`src/adapters/`)
Bridges between interfaces and concrete implementations:
#### CacheAdapter
- Adapts the Cache utility to IImageCache interface
- Allows cache implementation to be swapped
#### OptimizerAdapter
- Adapts ImageOptimizer utility to IImageOptimizer interface
- Enables different optimization strategies
### 4. Utils Layer (`src/utils/`)
Low-level utilities with specific responsibilities:
#### Cache
- Two-tier caching (memory + disk)
- TTL-based expiration
- Size-based cleanup
- Cache key generation
#### ImageOptimizer
- Image format conversion
- Quality optimization
- Size reduction calculations
- Sharp library integration
## Dependency Flow
```
┌─────────────────┐
│ MCP Server │
└────────┬────────┘
│
┌────────▼────────┐
│ Tools │ (image-generation.ts)
└────────┬────────┘
│ depends on
┌────────▼────────┐
│ Services │ (ImageGenerator)
└────────┬────────┘
│ depends on
┌────────▼────────┐
│ Interfaces │ (IImageCache, IImageOptimizer)
└────────┬────────┘
│ implemented by
┌────────▼────────┐
│ Adapters │ (CacheAdapter, OptimizerAdapter)
└────────┬────────┘
│ wraps
┌────────▼────────┐
│ Utils │ (Cache, ImageOptimizer)
└─────────────────┘
```
## Testing Architecture
The architecture supports comprehensive testing through:
1. **Dependency Injection**: All dependencies are injected, making mocking trivial
2. **Interface-based Design**: Tests can use mock implementations
3. **Pure Functions**: Many operations are pure, making them easy to test
4. **Isolated Components**: Each component can be tested independently
Example test structure:
```typescript
describe('ImageGenerator', () => {
let generator: ImageGenerator;
let mockClient: IOpenAIClient;
let mockCache: IImageCache;
beforeEach(() => {
// Create mocks
mockClient = { generateImage: vi.fn() };
mockCache = { get: vi.fn(), set: vi.fn() };
// Inject mocks
generator = new ImageGenerator(mockClient, mockCache, mockOptimizer);
});
it('should use cache when available', async () => {
// Test with mocked dependencies
});
});
```
## Extension Points
The architecture allows easy extension through:
1. **New Image Generators**: Implement `IImageGenerator` for new generation strategies
2. **Alternative Caching**: Implement `IImageCache` for Redis, Memcached, etc.
3. **Different Optimizers**: Implement `IImageOptimizer` for different optimization libraries
4. **Additional Clients**: Implement `IOpenAIClient` for other AI providers
## Benefits
1. **Maintainability**: Clear separation of concerns makes code easy to understand
2. **Testability**: 98%+ test coverage for core utilities
3. **Flexibility**: Easy to swap implementations
4. **Scalability**: Components can be scaled independently
5. **Type Safety**: Full TypeScript support with interfaces
## Future Considerations
1. **Event-Driven Architecture**: Consider event bus for decoupling
2. **Repository Pattern**: Abstract data access for image storage
3. **Strategy Pattern**: For different generation algorithms
4. **Plugin System**: Dynamic loading of processors
5. **Microservices**: Components could be separated into services