README.md•7.81 kB
# SmartThings MCP Server (Python)
A production-ready, Dockerized MCP (Model Context Protocol) server for controlling and querying SmartThings devices via Personal Access Token (PAT).
## Features
- ✅ **Fully Async**: Efficient async/await implementation with proper resource management
- ✅ **Comprehensive Logging**: Detailed logging for debugging and monitoring
- ✅ **Type Safety**: Full type hints and Pydantic validation
- ✅ **Error Handling**: Structured error handling with meaningful messages
- ✅ **Optimized API Calls**: Direct device lookups to minimize API requests
- ✅ **Docker Best Practices**: Non-root user, health checks, minimal image size
- ✅ **Resource Cleanup**: Proper HTTP client lifecycle management
## Project Structure
```
.
├── Dockerfile # Production-ready Docker image
├── docker-compose.yml # Docker Compose configuration
├── .dockerignore # Docker build exclusions
├── .env.example # Environment variable template
├── requirements.txt # Python dependencies
├── app/
│ ├── __init__.py
│ ├── main.py # MCP server and tool definitions
│ └── smartthings.py # SmartThings API client
└── README.md # This file
```
## Setup
### 1. Get a SmartThings Personal Access Token (PAT)
1. Visit https://account.smartthings.com/tokens
2. Click "Generate new token"
3. Give it a name (e.g., "MCP Server")
4. Select required scopes:
- `devices` (read and execute)
- `locations` (read)
5. Copy the generated token
### 2. Configure Environment
```bash
cp .env.example .env
# Edit .env and add your SMARTTHINGS_PAT
```
### 3. Build and Run
#### Using Docker
```bash
# Build the image
docker build -t smartthings_mcp:latest .
# Run with stdio transport
docker run --rm -i \
-e SMARTTHINGS_PAT=$SMARTTHINGS_PAT \
-e SMARTTHINGS_LOCATION_ID=$SMARTTHINGS_LOCATION_ID \
smartthings_mcp:latest
```
#### Using Docker Compose
```bash
# Build and run
docker compose build
docker compose run --rm smartthings-mcp python -m app.main
```
#### Local Development
```bash
# Install dependencies
pip install -r requirements.txt
# Set environment variables
export SMARTTHINGS_PAT="your_pat_here"
# Run the server
python -m app.main
```
## Available Tools
### `list_locations()`
List all SmartThings locations accessible with the current PAT.
**Returns**: List of location objects with `locationId`, `name`, and metadata.
---
### `list_rooms(locationId?)`
List all rooms in a SmartThings location.
**Parameters**:
- `locationId` (optional): The location ID. Falls back to `SMARTTHINGS_LOCATION_ID` env var or first available location.
**Returns**: List of room objects with `roomId`, `name`, and metadata.
---
### `list_devices(locationId?, roomId?)`
List all SmartThings devices, optionally filtered by location and/or room.
**Parameters**:
- `locationId` (optional): Filter devices by location
- `roomId` (optional): Filter devices by room
**Returns**: List of device objects with `deviceId`, `label`, `capabilities`, and metadata.
---
### `device_status({ deviceId?, name? })`
Get the current status of a SmartThings device.
**Parameters**:
- `deviceId` (optional): The unique device ID (preferred for efficiency)
- `name` (optional): Device name/label to search for (slower, searches all devices)
**Returns**: Device information with current status and flattened summary.
**Note**: Provide at least one of `deviceId` or `name`.
---
### `switch_device({ deviceId?, name?, command, component? })`
Turn a SmartThings switch device on or off.
**Parameters**:
- `deviceId` (optional): The unique device ID (preferred)
- `name` (optional): Device name/label to search for
- `command` (required): Either `"on"` or `"off"`
- `component` (optional): Device component (default: `"main"`)
**Returns**: Device information and command execution result.
---
### `fridge_status({ deviceId?, name? })`
Get detailed status of a SmartThings refrigerator/fridge device.
**Parameters**:
- `deviceId` (optional): The unique device ID
- `name` (optional): Device name/label to search for
**Returns**: Refrigerator-specific status summary including:
- Power state
- Refrigerator and freezer temperatures
- Door open/closed state
- Ice maker status
- Defrost mode
- All temperature, door, ice, humidity, and energy-related attributes
**Note**: If neither parameter is provided, automatically searches for devices with "fridge" or "refrigerator" in the name.
---
### `device_health(deviceId)`
Get the health status of a SmartThings device.
**Parameters**:
- `deviceId` (required): The unique device ID
**Returns**: Device health information including online/offline state.
## Architecture Improvements
### Async Design
- All tools are fully async for better concurrency
- Uses a single HTTP client with connection pooling
- Proper cleanup on shutdown via lifespan handler
### Error Handling
- Pydantic validation for all inputs
- Structured error messages
- Graceful fallbacks (e.g., health check returns "unknown" instead of failing)
### Performance Optimizations
- Direct device GET endpoint when using `deviceId` (avoids listing all devices)
- Efficient device search with early exit
- Connection reuse across all requests
### Security
- Runs as non-root user in Docker
- Minimal attack surface (no exposed ports)
- Token never logged
### Logging
- Comprehensive logging at INFO and DEBUG levels
- Structured log format with timestamps
- Request/response logging for debugging
## Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `SMARTTHINGS_PAT` | ✅ Yes | - | SmartThings Personal Access Token |
| `SMARTTHINGS_LOCATION_ID` | ❌ No | - | Default location ID for room queries |
| `SMARTTHINGS_BASE_URL` | ❌ No | `https://api.smartthings.com/v1` | SmartThings API base URL |
| `MCP_TRANSPORT` | ❌ No | `stdio` | MCP transport method |
## Development
### Running Tests
```bash
# Install dev dependencies
pip install pytest pytest-asyncio httpx
# Run tests
pytest
```
### Logging Levels
Set the log level via environment:
```bash
# Debug level (verbose)
export LOG_LEVEL=DEBUG
# Info level (default)
export LOG_LEVEL=INFO
# Warning level (quiet)
export LOG_LEVEL=WARNING
```
### Code Style
This project follows:
- PEP 8 style guide
- Type hints for all functions
- Docstrings for all public APIs
## Troubleshooting
### "SMARTTHINGS_PAT is required"
- Ensure you've set the `SMARTTHINGS_PAT` environment variable
- Check that your PAT has the correct scopes
### "No locations found"
- Verify your PAT has `locations:read` scope
- Check that you have at least one location in your SmartThings account
### "Device not found"
- Use `list_devices()` to see available devices
- Ensure the device name or ID is correct (case-insensitive for names)
- Check that the device is in the specified location/room if filters are applied
### HTTP Timeout Errors
- Increase timeout in `smartthings.py` (default: 15s)
- Check network connectivity to SmartThings API
## License
MIT License - feel free to use and modify as needed.
## Contributing
Contributions welcome! Please:
1. Add tests for new features
2. Update documentation
3. Follow existing code style
4. Ensure all tests pass
## Changelog
### v2.0.0 (Current)
- ✅ Full async/await implementation
- ✅ Comprehensive logging and error handling
- ✅ Optimized API calls with direct device lookups
- ✅ Docker best practices (non-root user, health checks)
- ✅ Pydantic validation for all inputs
- ✅ Proper resource cleanup
- ✅ Complete type hints and docstrings
### v1.0.0 (Original)
- Basic MCP server implementation
- Synchronous tool functions
- Basic error handling