SmartThingsMCP

A comprehensive FastMCP 2.0 server and client for interacting with SmartThings devices, locations, rooms, modes, scenes, and automation rules through the SmartThings API.

SmartThingsMCP Server and Client

A comprehensive FastMCP 2.0 server and client for interacting with SmartThings devices, locations, rooms, modes, scenes, and automation rules through the SmartThings API.

Overview

SmartThingsMCP provides:

  • FastMCP 2.0 Server: Exposes SmartThings API functionality as MCP tools
  • Smart Client: Python client with intelligent caching, async support, and multiple transport options
  • Modular Architecture: Organized by functionality (devices, locations, rooms, modes, scenes, rules)
  • Two-Level Caching: Both client and server-side caching for optimal performance
  • Multiple Transports: HTTP, SSE (Server-Sent Events), and STDIO support
  • OAuth 2.0 Authentication: Secure token-based authentication with SmartThings API

Components

Server

  • SmartThingsMCPServer.py: FastMCP 2.0 server exposing SmartThings API as MCP tools
  • modules/server/: Server tool implementations
    • devices.py: Device management tools (list, get, update, delete, execute commands, etc.)
    • locations.py: Location management tools (create, read, update, delete locations and rooms)
    • rooms.py: Room management tools (list, create, update, delete rooms)
    • modes.py: Mode management tools (list, get, set location modes)
    • scenes.py: Scene management tools (list, get, execute, create, update, delete scenes)
    • rules.py: Automation rule tools (list, get, create, update, delete, execute rules)
    • structure_tools.py: Structure generation tools for LLM integration
    • common.py: Shared utilities for API requests, URL building, and parameter filtering

Client

  • SmartThingsMCPClient.py: CLI for interacting with the MCP server
  • modules/client/: Client implementation
    • main.py: Main SmartThingsMCPClient class combining all mixins
    • base.py: BaseClient with transport handling and tool invocation
    • cache.py: CacheMixin with LRU caching, TTL management, and cache statistics
    • devices.py: DevicesMixin with device operation methods
    • locations.py: LocationsMixin with location and room methods
    • rooms.py: RoomsMixin with room-specific methods
    • modes.py: ModesMixin with mode management methods
    • rules.py: RulesMixin with rule management methods
    • scenes.py: ScenesMixin with scene management methods
    • utils.py: Utility functions for tool conversion and action execution
    • utils_ext.py: Extended utilities (note: currently unused)

API Endpoints

SmartThingsMCP exposes the following API endpoints as MCP tools:

Device Management

  • list_devices: Get a list of all devices (supports filtering by capability, location, room, or device ID)
  • get_device: Get details of a specific device
  • update_device: Update a device's label
  • delete_device: Delete a device
  • execute_command: Execute a command on a device component
  • get_device_status: Get the current status of a device
  • get_device_components: Get all components of a device
  • get_device_capabilities: Get capabilities of a device component
  • get_device_health: Get the health/connectivity status of a device
  • get_device_presentation: Get the UI presentation details of a device

Location Management

  • list_locations: Get a list of all locations
  • get_location: Get details of a specific location
  • create_location: Create a new location with coordinates and address information
  • update_location: Update location details (name, coordinates, address)
  • delete_location: Delete a location
  • get_location_rooms: Get all rooms in a location (convenience method)

Room Management

  • list_rooms: Get all rooms in a location
  • get_room: Get details of a specific room
  • create_room: Create a new room in a location
  • update_room: Update a room's name
  • delete_room: Delete a room from a location

Mode Management

  • list_modes: Get all available modes for a location
  • get_mode: Get details of a specific mode
  • get_current_mode: Get the currently active mode for a location
  • set_mode: Change the current mode for a location

Scene Management

  • list_scenes: Get all scenes (optionally filtered by location)
  • get_scene: Get details of a specific scene
  • execute_scene: Execute/run a scene
  • create_scene: Create a new scene with actions and visual properties
  • update_scene: Update an existing scene (name, icon, colors, actions)
  • delete_scene: Delete a scene

Rule Management

  • list_rules: Get all automation rules (optionally filtered by location)
  • get_rule: Get details of a specific rule
  • create_rule: Create a new automation rule with conditions and actions
  • update_rule: Update an existing rule (name, triggers, actions, enabled state)
  • delete_rule: Delete an automation rule
  • execute_rule: Manually trigger execution of a rule

Features

Intelligent Caching

Both the SmartThingsMCPClient and SmartThingsMCPServer include comprehensive caching to improve performance:

Client-Side Caching

The SmartThingsMCPClient provides:

  • Automatic caching of read-only operations (list_devices, list_locations, etc.)
  • TTL-based expiration (default: 5 minutes, configurable)
  • Smart cache invalidation on write operations (execute_command, update_device, etc.)
  • LRU eviction when cache is full
  • Cache statistics tracking (hits, misses, hit rate)
  • Green visual feedback: ✓ Cache hit: list_locations

See CACHING.md for detailed documentation.

Server-Side Caching

The SmartThingsMCPServer provides:

  • Automatic caching of all GET requests to SmartThings API
  • TTL-based expiration (default: 5 minutes)
  • Full cache invalidation on write operations (POST, PUT, DELETE)
  • LRU eviction when cache is full
  • Cache statistics tracking
  • Green visual feedback: ✓ Server cache hit: GET devices

See SERVER_CACHING.md for detailed documentation.

Two-Level Caching: When both client and server caching are active, you get maximum performance with two layers of caching!

Performance Benefits:

  • 60-80% reduction in API calls
  • 5-10x faster response times for cached data
  • Lower rate limit usage

Rule Management

The server provides comprehensive rule management capabilities:

  • list_rules: List all automation rules for a location
  • get_rule: Get details of a specific rule
  • create_rule: Create a new automation rule with conditions and actions
  • update_rule: Update an existing rule (name, triggers, actions, or enabled state)
  • delete_rule: Delete a rule
  • execute_rule: Manually execute a rule

Enable/Disable Rules:

# Disable a rule
client.update_rule(auth=token, rule_id="abc-123", enabled=False)

# Enable a rule
client.update_rule(auth=token, rule_id="abc-123", enabled=True)

The enabled parameter was added in December 2025 to support toggling rule state without deleting the rule.

Installation

Prerequisites

  • Python 3.8 or higher
  • pip or another Python package manager

Setup

  1. Install dependencies:
pip install -r requirements.txt

Required packages:

  • fastmcp>=2.0.0: FastMCP 2.0 framework for MCP server/client
  • requests>=2.28.0: HTTP library for SmartThings API calls
  1. Obtain a SmartThings API Token:
    • Visit SmartThings Developer Portal
    • Create a new API token with the following scopes:
      • r:devices:* (read devices)
      • w:devices:* (control devices)
      • r:locations:* (read locations)
      • w:locations:* (create/update locations)
      • r:rules:* (read rules, requires Enterprise account)
      • w:rules:* (write rules, requires Enterprise account)
      • r:scenes:* (read scenes)
      • x:scenes:* (execute scenes)

Authentication

SmartThingsMCP uses OAuth 2.0 bearer tokens for authentication with the SmartThings API.

Token Requirements

  • Must be a valid SmartThings API token (OAuth 2.0 bearer token)
  • Token must have appropriate scopes for the operations you want to perform
  • Token never expires when obtained from the SmartThings Developer Portal
  • Keep your token secure and never commit it to version control

Common Authentication Issues

401 Unauthorized:

Error calling tool list_devices: 401 Unauthorized
  • Verify the token is valid and not expired
  • Check that the token has been generated from SmartThings Developer Portal
  • Ensure you're passing the token with the --auth flag

403 Forbidden:

Error calling tool list_devices: 403 Forbidden
  • The token exists but lacks required scopes
  • Some features (like Rules) require an Enterprise SmartThings account
  • Grant additional scopes to the token in SmartThings Developer Portal

Scopes Required by Feature

FeatureRequired Scopes
List/Get Devicesr:devices:*
Control Devicesw:devices:*
List/Get Locationsr:locations:*
Create/Update/Delete Locationsw:locations:*
List/Get Scenesr:scenes:*
Execute Scenesx:scenes:*
Create/Update/Delete Scenesw:scenes:*
List/Get Rulesr:rules:*
Create/Update/Delete Rulesw:rules:*
List/Get/Set Modesr:locations:*

Getting Started

Starting the Server

  1. Start the SmartThingsMCP server:
# Start with HTTP transport (default) on port 8000
python SmartThingsMCPServer.py

# Custom port
python SmartThingsMCPServer.py -port 9000

# Using SSE transport
python SmartThingsMCPServer.py -transport sse

# Using STDIO transport
python SmartThingsMCPServer.py -transport stdio

# With auth token override (if needed)
python SmartThingsMCPServer.py -auth YOUR_TOKEN

Using the Command-Line Client

The SmartThingsMCPClient.py provides a CLI interface:

# List available tools
python SmartThingsMCPClient.py --transport http --port 8000 --action list_tools

# List all devices (requires auth token)
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action list_devices

# List devices with pretty-printed output
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action list_devices --pretty

# Get a specific device
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action get_device --params '{"device_id": "DEVICE_ID"}'

# Get device status
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action get_device_status --params '{"device_id": "DEVICE_ID"}'

# Execute a device command
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action execute_command --params '{
  "device_id": "DEVICE_ID",
  "component": "main",
  "capability": "switch",
  "command": "on"
}'

# List all locations
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action list_locations

# List all scenes
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action list_scenes

# Execute a scene
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action execute_scene --params '{"scene_id": "SCENE_ID"}'

# Create a new location
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action create_location --params '{
  "name": "Office",
  "country_code": "US",
  "region_code": "CA",
  "locality": "San Francisco"
}'

# List all modes for a location
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action list_modes --params '{"location_id": "LOCATION_ID"}'

# Set mode for a location
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action set_mode --params '{
  "location_id": "LOCATION_ID",
  "mode_id": "MODE_ID"
}'

# List all rules
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action list_rules

# Enable/disable a rule
python SmartThingsMCPClient.py --auth YOUR_TOKEN --action update_rule --params '{
  "rule_id": "RULE_ID",
  "enabled": false
}'

Client Options

--host: MCP server host (default: localhost)
--port: MCP server port (default: 8000)
--auth: SmartThings API authentication token (required for most operations)
--transport: Transport type - http, sse, or stdio (default: http)
--action: Action/tool to execute (required)
--params: JSON string of parameters for the action (default: {})
--pretty: Pretty-print JSON output (flag)

Using Python Client Programmatically

import asyncio
from modules.client.main import SmartThingsMCPClient

async def main():
    # Create client with caching enabled (default)
    client = SmartThingsMCPClient(
        host="localhost",
        port=8000,
        auth_token="YOUR_TOKEN",
        transport="http",
        enable_cache=True,        # Enable automatic caching
        cache_ttl=300,            # Cache for 5 minutes
        max_cache_size=1000       # Store up to 1000 cache entries
    )
    
    # List all devices
    devices = await client.list_devices(auth="YOUR_TOKEN")
    print(f"Found {len(devices['items'])} devices")
    
    # Get specific device
    device = await client.get_device(auth="YOUR_TOKEN", device_id="DEVICE_ID")
    print(f"Device: {device['label']}")
    
    # Execute device command
    result = await client.execute_command(
        auth="YOUR_TOKEN",
        device_id="DEVICE_ID",
        component="main",
        capability="switch",
        command="on"
    )
    
    # List locations
    locations = await client.list_locations(auth="YOUR_TOKEN")
    for loc in locations['items']:
        print(f"Location: {loc['name']}")
    
    # List and execute scenes
    scenes = await client.list_scenes(auth="YOUR_TOKEN")
    if scenes['items']:
        scene_id = scenes['items'][0]['sceneId']
        await client.execute_scene(auth="YOUR_TOKEN", scene_id=scene_id)
    
    # Manage rules
    rules = await client.list_rules(auth="YOUR_TOKEN")
    for rule in rules['items']:
        print(f"Rule: {rule['name']} - Enabled: {rule.get('enabled', True)}")
    
    # Check cache statistics (when caching is enabled)
    if hasattr(client, 'get_cache_stats'):
        stats = client.get_cache_stats()
        print(f"Cache hits: {stats['hits']}, misses: {stats['misses']}")

if __name__ == "__main__":
    asyncio.run(main())

Troubleshooting

Transport Configuration

SmartThingsMCP supports three transport mechanisms for client-server communication:

HTTP Transport (Recommended for most use cases)

  • Default port: 8000
  • URL pattern: http://localhost:8000/mcp
  • Best for: External integrations, LLM tools, web services
  • Advantages:
    • Simple HTTP/REST interface
    • Easy to debug with standard tools
    • Compatible with most firewalls
    • Stateless connections
# Server
python SmartThingsMCPServer.py -transport http -port 8000

# Client
python SmartThingsMCPClient.py --transport http --port 8000

SSE Transport (Server-Sent Events)

  • Default port: 8000
  • URL pattern: http://localhost:8000/sse
  • Best for: Real-time updates, event streaming, server push
  • Advantages:
    • Bidirectional communication
    • Event-driven architecture
    • Lower latency for updates
# Server
python SmartThingsMCPServer.py -transport sse -port 8000

# Client
python SmartThingsMCPClient.py --transport sse --port 8000

STDIO Transport (Direct integration)

  • No network overhead
  • Best for: Direct Python integration, embedded systems
  • Advantages:
    • No port/network configuration needed
    • Direct process communication
    • Lowest latency
# Server (runs in foreground)
python SmartThingsMCPServer.py -transport stdio

# Client
python SmartThingsMCPClient.py --transport stdio

Client Configuration

Cache Configuration

The SmartThingsMCPClient includes intelligent caching with full control:

client = SmartThingsMCPClient(
    host="localhost",
    port=8000,
    auth_token="YOUR_TOKEN",
    enable_cache=True,        # Enable caching
    cache_ttl=300,            # TTL in seconds (default: 5 min)
    max_cache_size=1000       # Max entries (default: 1000)
)

# Get cache statistics
stats = client.get_cache_stats()
print(f"Hit rate: {stats['hit_rate']:.2%}")

# Clear cache manually
client.clear_cache()

Cacheable Operations (automatic caching):

  • list_devices
  • get_device
  • list_locations
  • get_location
  • list_rooms
  • get_room
  • list_modes
  • get_mode
  • get_current_mode
  • list_scenes
  • get_scene
  • list_rules
  • get_rule

Cache-Invalidating Operations (automatic cache clearing):

  • update_device
  • delete_device
  • execute_command
  • create_location
  • update_location
  • delete_location
  • create_room
  • update_room
  • delete_room
  • set_mode
  • create_scene
  • update_scene
  • delete_scene
  • create_rule
  • update_rule
  • delete_rule
  • execute_rule
  • execute_scene

Logging

SmartThingsMCP uses Python's standard logging module:

import logging

# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('smartthings_mcp')

# Create client - will now output detailed logs
client = SmartThingsMCPClient(...)

Log levels:

  • DEBUG: Detailed diagnostic information
  • INFO: General informational messages (default)
  • WARNING: Warning messages
  • ERROR: Error messages
  • CRITICAL: Critical errors

Common Errors and Solutions

Common Errors and Solutions

Connection Errors

Error: Client failed to connect: Session terminated

Error calling tool list_devices: Client failed to connect: Session terminated

Causes and solutions:

  • Server is not running: Start the server with python SmartThingsMCPServer.py
  • Wrong transport type: Ensure client and server use same transport (http, sse, or stdio)
  • Wrong port: Verify port matches between client and server
  • Wrong host: Check hostname/IP address is correct
  • Network/firewall: Ensure port is open and accessible

Debugging:

# Check if server is running on expected port
netstat -tuln | grep 8000

# Test HTTP connectivity
curl http://localhost:8000/mcp

# View server logs
python SmartThingsMCPServer.py 2>&1 | head -20

Authentication Errors

Error: 1 validation error for list_devicesArguments: auth: Field required

Error executing tool list_devices: 1 validation error for list_devicesArguments
auth: Field required

Solutions:

  • Provide auth token with --auth YOUR_TOKEN flag
  • Ensure token is valid: verify in SmartThings Developer Portal
  • Check token has not expired
  • Verify token has required scopes

Example:

python SmartThingsMCPClient.py --auth "YOUR_VALID_TOKEN" --action list_devices

Error: 401 Unauthorized

Error calling tool list_devices: 401 Unauthorized

Solutions:

  • Token is invalid or expired
  • Token format is incorrect (must be OAuth 2.0 bearer token)
  • Generate new token from SmartThings Developer Portal

Error: 403 Forbidden

Error calling tool create_rule: 403 Forbidden - Access Denied

Solutions:

  • Token lacks required scopes for the operation
  • SmartThings account doesn't have access to feature (e.g., Rules API requires Enterprise)
  • Grant additional scopes in SmartThings Developer Portal

API Response Errors

Error: Device not found

{
  "error": "Device not found",
  "message": "No device found with ID: invalid-id"
}

Solutions:

  • Device ID doesn't exist
  • Device has been deleted
  • List devices first to get valid IDs: list_devices action

Error: Invalid component or capability

{
  "error": "Component not found",
  "message": "Component 'main' not found on device"
}

Solutions:

  • Check valid components: Use get_device_components to list available components
  • Check valid capabilities: Use get_device_capabilities with correct component_id
  • Device doesn't support the command you're trying to execute

Rate Limiting

Error: 429 Too Many Requests

Error calling tool list_devices: 429 Too Many Requests

Solutions:

  • SmartThings API rate limit exceeded
  • Enable caching to reduce API calls (enabled by default)
  • Increase cache TTL to keep data longer
  • Implement request throttling in your code

Check cache effectiveness:

stats = client.get_cache_stats()
print(f"Cache hits: {stats['hits']}")
print(f"Cache misses: {stats['misses']}")
print(f"Hit rate: {stats['hit_rate']:.2%}")

Cache Issues

Problem: Stale data in cache

Solutions:

  • Reduce cache TTL: cache_ttl=60 (1 minute)
  • Manually clear cache: client.clear_cache()
  • Disable cache if data must be real-time: enable_cache=False

Problem: Cache causing high memory usage

Solutions:

  • Reduce max cache size: max_cache_size=100
  • Reduce cache TTL to expire entries sooner
  • Periodically clear cache: client.clear_cache()

Advanced Usage

Custom Tool Implementation

You can extend SmartThingsMCP with custom tools by modifying the server modules:

# In modules/server/devices.py, add:
@server_instance.tool()
def custom_device_operation(auth: str, device_id: str) -> Dict[str, Any]:
    """Your custom operation description"""
    # Your implementation here
    return make_request(auth, "GET", build_device_url(device_id))

Integration with LLMs

SmartThingsMCP tools are designed to work with Language Models:

# Get formatted tool descriptions for LLM
tools = await client.list_tools()
tool_descriptions = [
    {
        "name": tool["name"],
        "description": tool["description"],
        "params": tool["parameters"]
    }
    for tool in tools
]

Error Handling

import asyncio

async def safe_device_operation():
    client = SmartThingsMCPClient(
        host="localhost",
        port=8000,
        auth_token="YOUR_TOKEN"
    )
    
    try:
        result = await client.get_device(
            auth="YOUR_TOKEN",
            device_id="DEVICE_ID"
        )
        return result
    except ValueError as e:
        print(f"Invalid input: {e}")
    except ConnectionError as e:
        print(f"Connection failed: {e}")
    except Exception as e:
        print(f"Unexpected error: {e}")
        
asyncio.run(safe_device_operation())

Performance Optimization

  1. Enable caching (default: enabled):

    # Already enabled by default
    client = SmartThingsMCPClient(enable_cache=True)
    
  2. Adjust cache TTL for your use case:

    # More frequent updates needed
    client = SmartThingsMCPClient(cache_ttl=60)  # 1 minute
    
    # Stable data, longer TTL
    client = SmartThingsMCPClient(cache_ttl=900)  # 15 minutes
    
  3. Use appropriate transport:

    • HTTP: Best for most scenarios
    • STDIO: Best latency for local integration
    • SSE: Best for real-time updates
  4. Batch operations:

    # Get all data at once rather than in loops
    devices = await client.list_devices(auth=token)
    for device in devices['items']:
        # Use data from the single list_devices call
        # Don't call get_device for each one if not needed
    

API Response Format

All API responses follow a consistent structure:

Success response:

{
  "items": [...],
  "pageProperties": {
    "currentPage": 1,
    "pageSize": 50,
    "totalCount": 100
  }
}

Single item response:

{
  "id": "unique-id",
  "label": "Device Name",
  "deviceTypeId": "type-id"
}

Error response:

{
  "requestId": "request-id",
  "errors": [
    {
      "code": "INVALID_PARAMETER",
      "message": "Detailed error message"
    }
  ]
}

Example Scripts

Monitor Device Status

import asyncio
from modules.client.main import SmartThingsMCPClient

async def monitor_devices(token):
    client = SmartThingsMCPClient(auth_token=token)
    
    # Get all devices
    devices = await client.list_devices(auth=token)
    
    for device in devices['items']:
        try:
            status = await client.get_device_status(
                auth=token,
                device_id=device['deviceId']
            )
            print(f"{device['label']}: {status}")
        except Exception as e:
            print(f"Error getting status for {device['label']}: {e}")

asyncio.run(monitor_devices("YOUR_TOKEN"))

Control Multiple Devices

import asyncio
from modules.client.main import SmartThingsMCPClient

async def control_devices(token, device_ids, command):
    client = SmartThingsMCPClient(auth_token=token)
    
    for device_id in device_ids:
        try:
            result = await client.execute_command(
                auth=token,
                device_id=device_id,
                component="main",
                capability="switch",
                command=command
            )
            print(f"Device {device_id}: {result}")
        except Exception as e:
            print(f"Error controlling {device_id}: {e}")

asyncio.run(control_devices("YOUR_TOKEN", ["device1", "device2"], "on"))

Scene Execution

import asyncio
from modules.client.main import SmartThingsMCPClient

async def execute_morning_routine(token):
    client = SmartThingsMCPClient(auth_token=token)
    
    # Get morning scene
    scenes = await client.list_scenes(auth=token)
    morning_scene = next(
        (s for s in scenes['items'] if 'morning' in s.get('sceneName', '').lower()),
        None
    )
    
    if morning_scene:
        result = await client.execute_scene(
            auth=token,
            scene_id=morning_scene['sceneId']
        )
        print(f"Executed scene: {result}")

asyncio.run(execute_morning_routine("YOUR_TOKEN"))

Environment Variables

You can optionally use environment variables for common settings:

export SMARTTHINGS_TOKEN="your-token-here"
export MCP_SERVER_HOST="localhost"
export MCP_SERVER_PORT="8000"
export MCP_TRANSPORT="http"

API Reference

Complete API reference is available through the MCP tools system. List all available tools:

python SmartThingsMCPClient.py --action list_tools --pretty

This will display all available tools with their parameters and descriptions.

Contributing

When extending SmartThingsMCP:

  1. Follow the existing module structure
  2. Add tools to appropriate module (devices.py, locations.py, etc.)
  3. Include proper docstrings with parameter and return descriptions
  4. Test with the client
  5. Update README.md with new tool documentation

License

See LICENSE file for license information.

Related Servers