386 lines
12 KiB
Markdown
386 lines
12 KiB
Markdown
# Home Assistant MCP Server - Architecture Documentation
|
|
|
|
## Overview
|
|
|
|
This document describes the architecture and design decisions for the Home Assistant MCP Server, which bridges the Model Context Protocol with Home Assistant's REST API.
|
|
|
|
## System Architecture
|
|
|
|
```
|
|
┌─────────────────┐
|
|
│ MCP Client │ (Claude Desktop, other MCP clients)
|
|
│ (LLM) │
|
|
└────────┬────────┘
|
|
│ MCP Protocol (stdio)
|
|
│
|
|
┌────────▼────────┐
|
|
│ MCP Server │
|
|
│ (This app) │
|
|
├─────────────────┤
|
|
│ • Resources │ Read-only data access via URIs
|
|
│ • Tools │ Action execution with parameters
|
|
│ • Type Safety │ Zod validation + TypeScript
|
|
└────────┬────────┘
|
|
│ HTTP + Bearer Auth
|
|
│
|
|
┌────────▼────────┐
|
|
│ Home Assistant │
|
|
│ REST API │
|
|
│ (Port 8123) │
|
|
└─────────────────┘
|
|
```
|
|
|
|
## Component Architecture
|
|
|
|
### 1. Core Components
|
|
|
|
#### `index.ts` - MCP Server
|
|
**Responsibilities:**
|
|
- Initialize MCP server with capabilities
|
|
- Handle resource listing and reading
|
|
- Handle tool listing and execution
|
|
- Manage request/response lifecycle
|
|
- Error handling and formatting
|
|
|
|
**Key Features:**
|
|
- Stdio transport for universal client compatibility
|
|
- Comprehensive error handling with MCP error codes
|
|
- Input validation for all tool parameters
|
|
- Connection verification on startup
|
|
|
|
#### `ha-client.ts` - Home Assistant Client
|
|
**Responsibilities:**
|
|
- Encapsulate all Home Assistant REST API calls
|
|
- Manage HTTP communication with axios
|
|
- Handle authentication headers
|
|
- Format and parse API responses
|
|
- Provide type-safe method interfaces
|
|
|
|
**Key Features:**
|
|
- Singleton client instance with configured base URL
|
|
- Bearer token authentication
|
|
- Timeout configuration (30s default)
|
|
- Comprehensive error formatting
|
|
- Type-safe response parsing
|
|
|
|
#### `types.ts` - Type Definitions
|
|
**Responsibilities:**
|
|
- Define TypeScript interfaces for all data structures
|
|
- Document API response formats
|
|
- Ensure type safety across the codebase
|
|
|
|
**Key Features:**
|
|
- Complete coverage of HA API response types
|
|
- Nested interface definitions for complex objects
|
|
- JSDoc comments for documentation
|
|
|
|
### 2. MCP Resource Design
|
|
|
|
Resources provide read-only access to Home Assistant data through URI-based addressing.
|
|
|
|
#### Resource URI Scheme
|
|
```
|
|
ha://states - All entity states
|
|
ha://states/{entity_id} - Specific entity state
|
|
ha://config - System configuration
|
|
ha://services - Available services
|
|
ha://events - Registered events
|
|
ha://components - Loaded components
|
|
ha://error_log - Error log entries
|
|
```
|
|
|
|
#### Resource Implementation Pattern
|
|
1. **Declaration**: Resource metadata in `ListResourcesRequestSchema` handler
|
|
2. **Reading**: URI parsing and data retrieval in `ReadResourceRequestSchema` handler
|
|
3. **Response**: JSON or text content with appropriate MIME type
|
|
|
|
#### Design Rationale
|
|
- **Static URIs**: Predictable, well-known endpoints for core data
|
|
- **Dynamic URIs**: Entity-specific URIs follow `ha://states/{entity_id}` pattern
|
|
- **MIME Types**: `application/json` for structured data, `text/plain` for logs
|
|
- **Caching**: Not implemented (always fresh data from HA)
|
|
|
|
### 3. MCP Tool Design
|
|
|
|
Tools allow LLMs to execute actions and state-changing operations.
|
|
|
|
#### Tool Categories
|
|
|
|
**Device Control:**
|
|
- `call_service` - Universal service execution interface
|
|
|
|
**State Management:**
|
|
- `get_state` - Read specific entity state
|
|
- `set_state` - Create or update entity state
|
|
|
|
**Event System:**
|
|
- `fire_event` - Trigger custom events
|
|
|
|
**Data Queries:**
|
|
- `get_history` - Historical state data
|
|
- `get_logbook` - Human-readable event logs
|
|
- `render_template` - Execute Jinja2 templates
|
|
|
|
**Media & Calendar:**
|
|
- `get_camera_image` - Camera snapshots
|
|
- `get_calendar_events` - Calendar data
|
|
|
|
#### Tool Schema Design
|
|
|
|
Each tool has a JSON Schema defining:
|
|
- **Required parameters**: Must be provided
|
|
- **Optional parameters**: Have defaults or are conditional
|
|
- **Type constraints**: String, number, boolean, object, array
|
|
- **Descriptions**: Clear, LLM-friendly explanations
|
|
|
|
Example:
|
|
```typescript
|
|
{
|
|
name: 'call_service',
|
|
description: 'Call a Home Assistant service...',
|
|
inputSchema: {
|
|
type: 'object',
|
|
properties: {
|
|
domain: { type: 'string', description: '...' },
|
|
service: { type: 'string', description: '...' },
|
|
service_data: { type: 'object', description: '...' },
|
|
},
|
|
required: ['domain', 'service']
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Tool Implementation Pattern
|
|
1. **Declaration**: Tool metadata and schema in `ListToolsRequestSchema` handler
|
|
2. **Execution**: Parameter extraction and validation in `CallToolRequestSchema` handler
|
|
3. **API Call**: Delegate to `HomeAssistantClient` method
|
|
4. **Response**: JSON-formatted result in text content
|
|
|
|
### 4. Authentication & Security
|
|
|
|
#### Authentication Flow
|
|
```
|
|
1. Server startup: Load HA_ACCESS_TOKEN from environment
|
|
2. Client initialization: Configure axios with Bearer token header
|
|
3. Every request: Axios automatically includes Authorization header
|
|
4. HA validation: Home Assistant validates token for each request
|
|
```
|
|
|
|
#### Security Measures
|
|
- **Token Storage**: Environment variables (never hardcoded)
|
|
- **Token Transmission**: HTTPS recommended for production
|
|
- **Token Scope**: Full Home Assistant access (same as UI)
|
|
- **Token Rotation**: Manual process (revoke + create new)
|
|
|
|
#### Environment Configuration
|
|
```bash
|
|
HA_BASE_URL=http://homeassistant.local:8123
|
|
HA_ACCESS_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGc...
|
|
```
|
|
|
|
Validated at startup with Zod schema:
|
|
```typescript
|
|
const CONFIG_SCHEMA = z.object({
|
|
HA_BASE_URL: z.string().url(),
|
|
HA_ACCESS_TOKEN: z.string().min(1),
|
|
});
|
|
```
|
|
|
|
### 5. Error Handling
|
|
|
|
#### Error Flow
|
|
```
|
|
1. Try operation
|
|
2. Catch error
|
|
3. Format error message
|
|
4. Throw McpError with appropriate code
|
|
5. MCP SDK sends error to client
|
|
```
|
|
|
|
#### Error Types
|
|
|
|
**Connection Errors:**
|
|
- No response from Home Assistant
|
|
- Network timeout
|
|
- Invalid base URL
|
|
|
|
**Authentication Errors:**
|
|
- Invalid access token (401)
|
|
- Token expired
|
|
- Missing Authorization header
|
|
|
|
**API Errors:**
|
|
- Entity not found (404)
|
|
- Invalid service call (400)
|
|
- Service execution failure
|
|
|
|
**MCP Errors:**
|
|
- Invalid request (unknown resource/tool)
|
|
- Internal error (unexpected failures)
|
|
|
|
#### Error Formatting
|
|
```typescript
|
|
static formatError(error: any): string {
|
|
if (axios.isAxiosError(error)) {
|
|
if (error.response) {
|
|
return `Home Assistant API error (${error.response.status}): ...`;
|
|
} else if (error.request) {
|
|
return `No response from Home Assistant. Check if HA is running...`;
|
|
}
|
|
}
|
|
return `Unexpected error: ${error.message}`;
|
|
}
|
|
```
|
|
|
|
### 6. Data Flow Examples
|
|
|
|
#### Example 1: Reading Entity State (Resource)
|
|
```
|
|
1. LLM client: Request resource ha://states/light.living_room
|
|
2. MCP Server: Parse URI, extract entity_id
|
|
3. MCP Server: Call haClient.getState('light.living_room')
|
|
4. HA Client: GET /api/states/light.living_room
|
|
5. Home Assistant: Validate token, fetch state, return JSON
|
|
6. HA Client: Parse response, return EntityState object
|
|
7. MCP Server: Format as MCP resource response
|
|
8. LLM client: Receive entity state data
|
|
```
|
|
|
|
#### Example 2: Calling Service (Tool)
|
|
```
|
|
1. LLM client: Call tool call_service with parameters
|
|
2. MCP Server: Validate parameters against schema
|
|
3. MCP Server: Extract domain, service, service_data
|
|
4. MCP Server: Call haClient.callService(...)
|
|
5. HA Client: POST /api/services/{domain}/{service}
|
|
6. Home Assistant: Execute service, return changed states
|
|
7. HA Client: Return state changes array
|
|
8. MCP Server: Format as tool response
|
|
9. LLM client: Receive execution result
|
|
```
|
|
|
|
## Design Decisions
|
|
|
|
### 1. Why Stdio Transport?
|
|
- **Universal compatibility**: Works with any MCP client
|
|
- **Simple integration**: No network configuration needed
|
|
- **Secure**: No exposed ports, runs as subprocess
|
|
- **Standard**: MCP reference implementation pattern
|
|
|
|
### 2. Why Resources AND Tools?
|
|
- **Semantic clarity**: Read vs. write operations are explicit
|
|
- **Optimization**: Clients can cache resource data
|
|
- **Discovery**: LLMs can explore available data sources
|
|
- **REST mapping**: Aligns with HTTP GET vs. POST semantics
|
|
|
|
### 3. Why TypeScript?
|
|
- **Type safety**: Catch errors at compile time
|
|
- **IDE support**: Excellent autocomplete and refactoring
|
|
- **Documentation**: Types serve as inline documentation
|
|
- **MCP SDK**: Official SDK is TypeScript-first
|
|
|
|
### 4. Why Axios vs. Fetch?
|
|
- **Error handling**: Better error detection and formatting
|
|
- **Interceptors**: Easy to add logging or retry logic
|
|
- **Timeout support**: Built-in timeout configuration
|
|
- **Request/response transformation**: Automatic JSON parsing
|
|
|
|
### 5. Why Not WebSocket?
|
|
- **Scope**: Initial version focuses on request-response
|
|
- **Complexity**: Stdio + WebSocket would require connection management
|
|
- **Future enhancement**: Can be added for real-time updates
|
|
|
|
## Extension Points
|
|
|
|
### Adding New Resources
|
|
1. Add resource definition to `ListResourcesRequestSchema`
|
|
2. Add URI handler to `ReadResourceRequestSchema`
|
|
3. Add HA client method if needed
|
|
4. Add type definitions if needed
|
|
|
|
### Adding New Tools
|
|
1. Add tool definition to `ListToolsRequestSchema`
|
|
2. Add execution handler to `CallToolRequestSchema`
|
|
3. Add HA client method if needed
|
|
4. Add type definitions if needed
|
|
|
|
### Supporting New HA APIs
|
|
1. Define TypeScript interfaces in `types.ts`
|
|
2. Add client methods to `HomeAssistantClient`
|
|
3. Expose as resource or tool in MCP server
|
|
4. Update documentation
|
|
|
|
## Performance Considerations
|
|
|
|
### Latency Sources
|
|
1. **MCP Protocol**: Minimal (stdio, no serialization overhead)
|
|
2. **HTTP Request**: Network RTT + HA processing (typically 10-100ms)
|
|
3. **JSON Parsing**: Minimal (native V8 parser)
|
|
|
|
### Optimization Strategies
|
|
- **Batch operations**: Use service calls that accept multiple entities
|
|
- **Minimal responses**: Use history API filters to reduce data transfer
|
|
- **Template rendering**: Offload complex queries to HA's template engine
|
|
|
|
### Scalability
|
|
- **Stateless design**: Each request is independent
|
|
- **No caching**: Always fresh data (trade-off for simplicity)
|
|
- **Connection pooling**: Axios reuses HTTP connections
|
|
|
|
## Testing Strategy
|
|
|
|
### Unit Tests (Future)
|
|
- Mock HA client responses
|
|
- Test MCP request handling
|
|
- Validate error formatting
|
|
- Test URI parsing
|
|
|
|
### Integration Tests (Future)
|
|
- Test against Home Assistant demo instance
|
|
- Verify all API endpoints
|
|
- Test authentication flows
|
|
- Test error scenarios
|
|
|
|
### Manual Testing
|
|
- Use MCP Inspector tool
|
|
- Test with Claude Desktop
|
|
- Verify against real Home Assistant instance
|
|
|
|
## Future Enhancements
|
|
|
|
### Priority 1: Core Functionality
|
|
- [ ] WebSocket support for real-time state updates
|
|
- [ ] Connection pooling and retry logic
|
|
- [ ] Comprehensive error logging
|
|
|
|
### Priority 2: Developer Experience
|
|
- [ ] Unit test coverage
|
|
- [ ] Integration test suite
|
|
- [ ] CLI for testing tools directly
|
|
- [ ] Debug mode with verbose logging
|
|
|
|
### Priority 3: Advanced Features
|
|
- [ ] Entity discovery with caching
|
|
- [ ] Service schema validation
|
|
- [ ] Template validation and preview
|
|
- [ ] Bulk operations tool
|
|
- [ ] Scene and script management tools
|
|
|
|
### Priority 4: Production Readiness
|
|
- [ ] Rate limiting
|
|
- [ ] Metrics and monitoring
|
|
- [ ] Health check endpoint
|
|
- [ ] Graceful shutdown handling
|
|
- [ ] Configuration validation UI
|
|
|
|
## Conclusion
|
|
|
|
This architecture prioritizes:
|
|
1. **Simplicity**: Easy to understand and extend
|
|
2. **Type safety**: Catch errors early
|
|
3. **Separation of concerns**: Clear component boundaries
|
|
4. **MCP best practices**: Follow official patterns
|
|
5. **Extensibility**: Easy to add new capabilities
|
|
|
|
The design balances functionality with maintainability, providing a solid foundation for Home Assistant + MCP integration.
|