Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adbf33d3c7 | ||
|
|
a3927ba66c | ||
|
|
2120685cdf | ||
|
|
e17787fa0f | ||
|
|
76bd1582e7 | ||
|
|
8a3025081a | ||
|
|
15f5918e20 | ||
|
|
09a500904c | ||
|
|
a323b1e9d0 | ||
|
|
7b742858db | ||
|
|
ae56716a86 | ||
|
|
a826ef80ec | ||
|
|
5e934904c8 | ||
|
|
3e9abaff6e | ||
| cc1c0cef8d |
194
README.md
194
README.md
@ -1,7 +1,5 @@
|
||||
# MCP Bridge - Home Assistant Integration
|
||||
|
||||
[](https://github.com/custom-components/hacs)
|
||||
|
||||
Bridge integration between Home Assistant and MCP (Model Context Protocol) servers, enabling AI agents to control your smart home.
|
||||
|
||||
## Features
|
||||
@ -18,29 +16,79 @@ MCP (Model Context Protocol) is Anthropic's protocol for connecting AI agents to
|
||||
|
||||
## Installation
|
||||
|
||||
### Via HACS (Recommended)
|
||||
**Note:** This integration is hosted on Gitea (not GitHub), so it's not available via HACS. Instead, use one of the automated installation methods below.
|
||||
|
||||
1. **Add Custom Repository**
|
||||
- Open HACS in Home Assistant
|
||||
- Click the three dots (⋮) in top right
|
||||
- Select "Custom repositories"
|
||||
- Repository: `http://your-gitea-server/username/homeassistant-mcp-bridge`
|
||||
- Category: **Integration**
|
||||
- Click "Add"
|
||||
### Method 1: One-Line Install/Update (Easiest)
|
||||
|
||||
2. **Install**
|
||||
- HACS → Integrations
|
||||
- Click "+ Explore & Download Repositories"
|
||||
- Search for "MCP Bridge"
|
||||
- Click "Download"
|
||||
- Restart Home Assistant
|
||||
Run this command in your Home Assistant terminal (requires Terminal & SSH addon or SSH access):
|
||||
|
||||
### Manual Installation
|
||||
```bash
|
||||
curl -sSL http://your-gitea.local:3000/username/homeassistant-mcp-bridge/raw/branch/main/quick_install.sh | \
|
||||
GITEA_URL=http://your-gitea.local:3000 \
|
||||
REPO=username/homeassistant-mcp-bridge \
|
||||
bash
|
||||
```
|
||||
|
||||
1. Copy `custom_components/mcp_bridge` to your Home Assistant config directory
|
||||
2. Restart Home Assistant
|
||||
**Important:**
|
||||
- Replace `your-gitea.local:3000` with your Gitea server address
|
||||
- Replace `username` with your Gitea username
|
||||
- Replace `main` with your branch name if different
|
||||
|
||||
See [INSTALLATION_GUIDE.md](INSTALLATION_GUIDE.md) for detailed instructions.
|
||||
After installation completes, restart Home Assistant.
|
||||
|
||||
### Method 2: Home Assistant Auto-Update Button
|
||||
|
||||
Add this to your `configuration.yaml` to create an update button/service:
|
||||
|
||||
```yaml
|
||||
shell_command:
|
||||
update_mcp_bridge: >
|
||||
curl -fsSL http://your-gitea.local:3000/username/homeassistant-mcp-bridge/raw/branch/main/quick_install.sh |
|
||||
GITEA_URL=http://your-gitea.local:3000
|
||||
REPO=username/homeassistant-mcp-bridge
|
||||
bash
|
||||
|
||||
script:
|
||||
update_mcp_bridge:
|
||||
alias: "Update MCP Bridge Integration"
|
||||
sequence:
|
||||
- service: shell_command.update_mcp_bridge
|
||||
- delay:
|
||||
seconds: 5
|
||||
- service: persistent_notification.create
|
||||
data:
|
||||
title: "MCP Bridge Updated"
|
||||
message: "Integration updated from Gitea. Please restart Home Assistant."
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
- Go to Developer Tools → Services
|
||||
- Select `script.update_mcp_bridge`
|
||||
- Click "Call Service"
|
||||
- Restart Home Assistant after update
|
||||
|
||||
**Optional Dashboard Button:**
|
||||
```yaml
|
||||
type: button
|
||||
name: Update MCP Bridge
|
||||
icon: mdi:download
|
||||
tap_action:
|
||||
action: call-service
|
||||
service: script.update_mcp_bridge
|
||||
```
|
||||
|
||||
### Method 3: Manual Installation
|
||||
|
||||
1. Download these files from your Gitea repository:
|
||||
- `custom_components/mcp_bridge/__init__.py`
|
||||
- `custom_components/mcp_bridge/manifest.json`
|
||||
- `custom_components/mcp_bridge/services.yaml`
|
||||
|
||||
2. Copy to `/config/custom_components/mcp_bridge/`
|
||||
|
||||
3. Restart Home Assistant
|
||||
|
||||
See [INSTALLATION_GUIDE.md](INSTALLATION_GUIDE.md) for detailed manual steps.
|
||||
|
||||
## Quick Start
|
||||
|
||||
@ -108,8 +156,7 @@ Returns scripts marked with `mcp_accessible` label, including their parameters.
|
||||
"speed": {
|
||||
"name": "Speed",
|
||||
"description": "0=off, 1=low, 2=medium, 3=high",
|
||||
"required": true,
|
||||
"selector": {"number": {"min": 0, "max": 3}}
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -125,6 +172,17 @@ Get detailed metadata for a specific entity.
|
||||
**Parameters:**
|
||||
- `entity_id`: The entity to query
|
||||
|
||||
## Updating the Integration
|
||||
|
||||
**Using Auto-Update Script/Button:**
|
||||
- Just run the install command again or call the `script.update_mcp_bridge` service
|
||||
- Restart Home Assistant after update
|
||||
|
||||
**Manual Update:**
|
||||
- Download the latest files from Gitea
|
||||
- Replace files in `/config/custom_components/mcp_bridge/`
|
||||
- Restart Home Assistant
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
@ -160,24 +218,46 @@ No configuration required! The integration automatically loads on Home Assistant
|
||||
## Compatibility
|
||||
|
||||
- **Home Assistant**: 2024.1.0 or newer
|
||||
- **HACS**: Any version
|
||||
- **Python**: 3.11+
|
||||
- **Installation**: Requires SSH/Terminal access or manual file management
|
||||
|
||||
## Documentation
|
||||
## Troubleshooting
|
||||
|
||||
- [Installation Guide](INSTALLATION_GUIDE.md) - Detailed setup instructions
|
||||
- [Integration README](custom_components/mcp_bridge/README.md) - Technical details
|
||||
- [Gitea Setup Guide](GITEA_SETUP_GUIDE.md) - For hosting on your own Gitea
|
||||
### Installation Script Fails
|
||||
|
||||
**Check:**
|
||||
- Home Assistant can reach your Gitea server
|
||||
- Terminal addon has network access
|
||||
- URLs are correct (server address, username, repo name)
|
||||
|
||||
**Test connectivity:**
|
||||
```bash
|
||||
curl http://your-gitea.local:3000/username/homeassistant-mcp-bridge
|
||||
```
|
||||
|
||||
### Integration Not Loading
|
||||
|
||||
**Check logs:**
|
||||
- Settings → System → Logs
|
||||
- Filter by "mcp_bridge"
|
||||
|
||||
**Common issues:**
|
||||
- Files not in correct location (`/config/custom_components/mcp_bridge/`)
|
||||
- Python syntax errors (check logs)
|
||||
- Didn't restart Home Assistant after installation
|
||||
|
||||
### Services Not Showing
|
||||
|
||||
**Verify installation:**
|
||||
```bash
|
||||
ls -la /config/custom_components/mcp_bridge/
|
||||
```
|
||||
|
||||
Should show: `__init__.py`, `manifest.json`, `services.yaml`
|
||||
|
||||
## Development
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
pytest custom_components/mcp_bridge/test_init.py
|
||||
```
|
||||
|
||||
### Project Structure
|
||||
### Repository Structure
|
||||
|
||||
```
|
||||
homeassistant-mcp-bridge/
|
||||
@ -186,31 +266,46 @@ homeassistant-mcp-bridge/
|
||||
│ ├── __init__.py # Core integration logic
|
||||
│ ├── manifest.json # Integration metadata
|
||||
│ ├── services.yaml # Service definitions
|
||||
│ └── README.md # Integration docs
|
||||
├── hacs.json # HACS configuration
|
||||
├── info.md # HACS UI description
|
||||
└── README.md # This file
|
||||
│ └── test_init.py # Unit tests
|
||||
├── quick_install.sh # One-line installer
|
||||
├── install_mcp_bridge.sh # Detailed installer
|
||||
├── ha_shell_command.yaml # HA shell command config
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Contributing
|
||||
### Running Tests
|
||||
|
||||
This is a personal project for homelab use, but suggestions and improvements are welcome!
|
||||
```bash
|
||||
pytest custom_components/mcp_bridge/test_init.py
|
||||
```
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Test thoroughly
|
||||
5. Submit a pull request
|
||||
## Versioning
|
||||
|
||||
Check your installed version:
|
||||
```yaml
|
||||
service: mcp_bridge.get_entity_metadata
|
||||
data:
|
||||
entity_id: sensor.any_sensor # Any entity
|
||||
```
|
||||
|
||||
The response will show the integration version in logs.
|
||||
|
||||
Or check `manifest.json`:
|
||||
```bash
|
||||
cat /config/custom_components/mcp_bridge/manifest.json | grep version
|
||||
```
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [x] Basic entity exposure
|
||||
- [x] Script discovery with labels
|
||||
- [x] Complete metadata support
|
||||
- [x] Automated install/update script
|
||||
- [ ] Real-time state updates via WebSocket
|
||||
- [ ] Custom entity tags beyond voice assistant exposure
|
||||
- [ ] Integration config flow (UI-based setup)
|
||||
- [ ] Service call history/logging
|
||||
- [ ] Version checking and update notifications
|
||||
|
||||
## License
|
||||
|
||||
@ -218,13 +313,14 @@ MIT License - Use freely in your homelab!
|
||||
|
||||
## Support
|
||||
|
||||
- **Issues**: [Gitea Issues](http://your-gitea-server/username/homeassistant-mcp-bridge/issues)
|
||||
- **Issues**: Report on your Gitea instance
|
||||
- **Logs**: Settings → System → Logs (filter by "mcp_bridge")
|
||||
- **Questions**: Check the detailed guides in the repository
|
||||
|
||||
## Acknowledgments
|
||||
## Related Projects
|
||||
|
||||
- Built for use with [Anthropic's MCP](https://modelcontextprotocol.io/)
|
||||
- Designed to work with n8n workflows
|
||||
- **MCP Server**: Coming next - connects to this integration
|
||||
- **n8n Integration**: Workflow automation with AI agents
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,143 +0,0 @@
|
||||
# MCP Bridge - Home Assistant Custom Integration
|
||||
|
||||
This integration provides services to expose filtered entities and custom scripts to MCP (Model Context Protocol) servers, enabling AI agents to control your Home Assistant instance.
|
||||
|
||||
## Installation
|
||||
|
||||
### Via HACS (Recommended)
|
||||
|
||||
1. **Install HACS** (if not already installed): https://hacs.xyz/docs/setup/download
|
||||
|
||||
2. **Add this repository as a custom repository:**
|
||||
- Open HACS in Home Assistant
|
||||
- Click the three dots in the top right
|
||||
- Select "Custom repositories"
|
||||
- Add your Gitea URL: `http://your-gitea-server/username/homeassistant-mcp-bridge`
|
||||
- Category: Integration
|
||||
- Click "Add"
|
||||
|
||||
3. **Install the integration:**
|
||||
- Go to HACS → Integrations
|
||||
- Click "+ Explore & Download Repositories"
|
||||
- Search for "MCP Bridge"
|
||||
- Click "Download"
|
||||
- Restart Home Assistant
|
||||
|
||||
### Manual Installation
|
||||
|
||||
1. Copy the `mcp_bridge` folder to your Home Assistant `custom_components` directory:
|
||||
```
|
||||
/config/custom_components/mcp_bridge/
|
||||
```
|
||||
|
||||
2. Restart Home Assistant
|
||||
|
||||
3. The integration will be automatically loaded (no configuration needed)
|
||||
|
||||
## Services
|
||||
|
||||
### `mcp_bridge.get_exposed_entities`
|
||||
|
||||
Returns all entities that are exposed to voice assistants.
|
||||
|
||||
**Returns:**
|
||||
```json
|
||||
{
|
||||
"entities": [...],
|
||||
"count": 42
|
||||
}
|
||||
```
|
||||
|
||||
### `mcp_bridge.get_exposed_scripts`
|
||||
|
||||
Returns all custom scripts marked with the `mcp_accessible` label.
|
||||
|
||||
**Returns:**
|
||||
```json
|
||||
{
|
||||
"scripts": [...],
|
||||
"count": 1
|
||||
}
|
||||
```
|
||||
|
||||
### `mcp_bridge.get_entity_metadata`
|
||||
|
||||
Get detailed metadata for a specific entity.
|
||||
|
||||
**Parameters:**
|
||||
- `entity_id` (required): The entity to query
|
||||
|
||||
**Returns:**
|
||||
```json
|
||||
{
|
||||
"entity_id": "light.living_room",
|
||||
"state": "on",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Exposing Entities to AI Agent
|
||||
|
||||
1. Go to Settings → Voice Assistants → Expose
|
||||
2. Toggle entities you want the AI agent to control
|
||||
3. These entities will be returned by `mcp_bridge.get_exposed_entities`
|
||||
|
||||
### Exposing Scripts to AI Agent
|
||||
|
||||
1. Create your custom script in HA
|
||||
2. Add the label `mcp_accessible` to the script:
|
||||
- Go to Settings → Automations & Scenes → Scripts
|
||||
- Click on your script
|
||||
- Click the settings icon (top right)
|
||||
- Under "Labels", add `mcp_accessible`
|
||||
3. The script will now be returned by `mcp_bridge.get_exposed_scripts`
|
||||
|
||||
## Example: Marking a Script as MCP-Accessible
|
||||
|
||||
```yaml
|
||||
# In your scripts.yaml or via UI
|
||||
set_master_bedroom_fan_speed:
|
||||
alias: "Set Master Bedroom Fan Speed"
|
||||
description: "[MCP] Set the speed of the master bedroom fan."
|
||||
# Add label via UI: Settings → Scripts → (your script) → Labels → mcp_accessible
|
||||
fields:
|
||||
fan_state:
|
||||
name: Fan speed
|
||||
description: "0=off, 1=low, 2=medium, 3=high"
|
||||
required: true
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 3
|
||||
step: 1
|
||||
sequence:
|
||||
# Your script actions here
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
You can test the services in Home Assistant's Developer Tools → Services:
|
||||
|
||||
1. Select `mcp_bridge.get_exposed_entities`
|
||||
2. Click "Call Service"
|
||||
3. View the response in the "Response" tab
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**No entities returned:**
|
||||
- Make sure you've exposed entities via Settings → Voice Assistants → Expose
|
||||
|
||||
**No scripts returned:**
|
||||
- Ensure your scripts have the `mcp_accessible` label
|
||||
- Check Home Assistant logs for errors: Settings → System → Logs
|
||||
|
||||
**Integration not loading:**
|
||||
- Check the logs for errors
|
||||
- Ensure the folder structure is correct
|
||||
- Restart Home Assistant
|
||||
|
||||
## Version
|
||||
|
||||
0.1.0 - Initial release
|
||||
@ -1,171 +1,173 @@
|
||||
"""MCP Bridge integration for Home Assistant.
|
||||
"""MCP Bridge integration for Home Assistant."""
|
||||
from __future__ import annotations
|
||||
|
||||
This integration provides services to expose filtered entities and scripts
|
||||
to MCP (Model Context Protocol) servers for AI agent control.
|
||||
"""
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.const import CONF_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers import area_registry as ar
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.components.script import async_get_script_fields
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = "mcp_bridge"
|
||||
|
||||
# Label to mark scripts as MCP-accessible
|
||||
MCP_ACCESSIBLE_LABEL = "mcp_accessible"
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the MCP Bridge integration."""
|
||||
_LOGGER.info("Setting up MCP Bridge integration")
|
||||
|
||||
async def get_exposed_entities(call: ServiceCall) -> dict[str, Any]:
|
||||
"""Get all entities exposed to voice assistants."""
|
||||
entity_reg = er.async_get(hass)
|
||||
|
||||
exposed_entities = []
|
||||
|
||||
# Iterate through all entities
|
||||
for entity_id, state in hass.states.async_all():
|
||||
entity_entry = entity_reg.async_get(entity_id)
|
||||
|
||||
# Check if entity is exposed to conversation (voice assistant)
|
||||
if entity_entry and entity_entry.options.get("conversation", {}).get("should_expose", False):
|
||||
# Get area name if available
|
||||
area_name = None
|
||||
if entity_entry.area_id:
|
||||
area_reg = hass.helpers.area_registry.async_get(hass)
|
||||
area = area_reg.async_get_area(entity_entry.area_id)
|
||||
if area:
|
||||
area_name = area.name
|
||||
|
||||
# Build entity data
|
||||
entity_data = {
|
||||
"entity_id": entity_id,
|
||||
area_reg = ar.async_get(hass)
|
||||
|
||||
entities: list[dict[str, Any]] = []
|
||||
|
||||
for state in hass.states.async_all():
|
||||
entity_entry = entity_reg.async_get(state.entity_id)
|
||||
if not entity_entry:
|
||||
continue
|
||||
|
||||
if not entity_entry.options.get("conversation", {}).get(
|
||||
"should_expose", False
|
||||
):
|
||||
continue
|
||||
|
||||
area_name = None
|
||||
if entity_entry.area_id:
|
||||
area = area_reg.async_get_area(entity_entry.area_id)
|
||||
if area:
|
||||
area_name = area.name
|
||||
|
||||
entities.append(
|
||||
{
|
||||
"entity_id": state.entity_id,
|
||||
"state": state.state,
|
||||
"friendly_name": state.attributes.get("friendly_name", entity_id),
|
||||
"friendly_name": state.attributes.get(
|
||||
"friendly_name", state.entity_id
|
||||
),
|
||||
"area": area_name,
|
||||
"domain": entity_id.split(".")[0],
|
||||
"domain": state.entity_id.split(".", 1)[0],
|
||||
"device_class": state.attributes.get("device_class"),
|
||||
"supported_features": state.attributes.get("supported_features", 0),
|
||||
"supported_features": state.attributes.get(
|
||||
"supported_features", 0
|
||||
),
|
||||
"last_changed": state.last_changed.isoformat(),
|
||||
"last_updated": state.last_updated.isoformat(),
|
||||
"attributes": dict(state.attributes)
|
||||
"attributes": dict(state.attributes),
|
||||
}
|
||||
|
||||
exposed_entities.append(entity_data)
|
||||
|
||||
_LOGGER.debug(f"Found {len(exposed_entities)} exposed entities")
|
||||
|
||||
)
|
||||
|
||||
return {
|
||||
"entities": exposed_entities,
|
||||
"count": len(exposed_entities)
|
||||
"entities": entities,
|
||||
"count": len(entities),
|
||||
}
|
||||
|
||||
async def get_exposed_scripts(call: ServiceCall) -> dict[str, Any]:
|
||||
"""Get all scripts marked as MCP-accessible."""
|
||||
entity_reg = er.async_get(hass)
|
||||
|
||||
exposed_scripts = []
|
||||
|
||||
# Get all script entities
|
||||
scripts: list[dict[str, Any]] = []
|
||||
|
||||
for entity_id in hass.states.async_entity_ids("script"):
|
||||
entity_entry = entity_reg.async_get(entity_id)
|
||||
|
||||
# Check if script has the mcp_accessible label
|
||||
if entity_entry and MCP_ACCESSIBLE_LABEL in entity_entry.labels:
|
||||
state = hass.states.get(entity_id)
|
||||
if not state:
|
||||
continue
|
||||
|
||||
# Get script attributes
|
||||
script_data = {
|
||||
if not entity_entry:
|
||||
continue
|
||||
|
||||
if MCP_ACCESSIBLE_LABEL not in entity_entry.labels:
|
||||
continue
|
||||
|
||||
fields = await async_get_script_fields(hass, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
||||
scripts.append(
|
||||
{
|
||||
"entity_id": entity_id,
|
||||
"friendly_name": state.attributes.get("friendly_name", entity_id),
|
||||
"description": state.attributes.get("description", ""),
|
||||
"fields": state.attributes.get("fields", {}),
|
||||
"friendly_name": state.attributes.get(
|
||||
"friendly_name", entity_id
|
||||
)
|
||||
if state
|
||||
else entity_id,
|
||||
"description": state.attributes.get("description", "")
|
||||
if state
|
||||
else "",
|
||||
"fields": fields or {},
|
||||
}
|
||||
|
||||
exposed_scripts.append(script_data)
|
||||
|
||||
_LOGGER.debug(f"Found {len(exposed_scripts)} exposed scripts")
|
||||
|
||||
)
|
||||
|
||||
return {
|
||||
"scripts": exposed_scripts,
|
||||
"count": len(exposed_scripts)
|
||||
"scripts": scripts,
|
||||
"count": len(scripts),
|
||||
}
|
||||
|
||||
|
||||
async def get_entity_metadata(call: ServiceCall) -> dict[str, Any]:
|
||||
"""Get detailed metadata for a specific entity."""
|
||||
entity_id = call.data.get(CONF_ENTITY_ID)
|
||||
|
||||
if not entity_id:
|
||||
_LOGGER.error("entity_id not provided")
|
||||
return {"error": "entity_id required"}
|
||||
|
||||
return {"error": "entity_id is required"}
|
||||
|
||||
entity_reg = er.async_get(hass)
|
||||
entity_entry = entity_reg.async_get(entity_id)
|
||||
area_reg = ar.async_get(hass)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
||||
if not state:
|
||||
_LOGGER.error(f"Entity {entity_id} not found")
|
||||
return {"error": f"Entity {entity_id} not found"}
|
||||
|
||||
# Get area name if available
|
||||
|
||||
entity_entry = entity_reg.async_get(entity_id)
|
||||
|
||||
area_name = None
|
||||
if entity_entry and entity_entry.area_id:
|
||||
area_reg = hass.helpers.area_registry.async_get(hass)
|
||||
area = area_reg.async_get_area(entity_entry.area_id)
|
||||
if area:
|
||||
area_name = area.name
|
||||
|
||||
metadata = {
|
||||
|
||||
data: dict[str, Any] = {
|
||||
"entity_id": entity_id,
|
||||
"state": state.state,
|
||||
"friendly_name": state.attributes.get("friendly_name", entity_id),
|
||||
"friendly_name": state.attributes.get(
|
||||
"friendly_name", entity_id
|
||||
),
|
||||
"area": area_name,
|
||||
"domain": entity_id.split(".")[0],
|
||||
"domain": entity_id.split(".", 1)[0],
|
||||
"device_class": state.attributes.get("device_class"),
|
||||
"supported_features": state.attributes.get("supported_features", 0),
|
||||
"supported_features": state.attributes.get(
|
||||
"supported_features", 0
|
||||
),
|
||||
"last_changed": state.last_changed.isoformat(),
|
||||
"last_updated": state.last_updated.isoformat(),
|
||||
"attributes": dict(state.attributes)
|
||||
"attributes": dict(state.attributes),
|
||||
}
|
||||
|
||||
|
||||
if entity_entry:
|
||||
metadata["labels"] = list(entity_entry.labels)
|
||||
metadata["is_exposed_to_conversation"] = entity_entry.options.get(
|
||||
data["labels"] = list(entity_entry.labels)
|
||||
data["is_exposed_to_conversation"] = entity_entry.options.get(
|
||||
"conversation", {}
|
||||
).get("should_expose", False)
|
||||
|
||||
return metadata
|
||||
|
||||
# Register services
|
||||
return data
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
"get_exposed_entities",
|
||||
get_exposed_entities,
|
||||
schema=None
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
"get_exposed_scripts",
|
||||
get_exposed_scripts,
|
||||
schema=None
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
"get_entity_metadata",
|
||||
get_entity_metadata,
|
||||
schema=None
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
|
||||
|
||||
_LOGGER.info("MCP Bridge services registered successfully")
|
||||
|
||||
return True
|
||||
@ -4,7 +4,9 @@
|
||||
"documentation": "https://www.git.quarantinedstudio.com/mvezina/homeassistant-mcp-bridge",
|
||||
"issue_tracker": "https://www.git.quarantinedstudio.com/mvezina/homeassistant-mcp-bridge/issues",
|
||||
"requirements": [],
|
||||
"codeowners": [],
|
||||
"codeowners": [
|
||||
"@mvezina"
|
||||
],
|
||||
"version": "0.1.0",
|
||||
"iot_class": "local_polling",
|
||||
"config_flow": false
|
||||
|
||||
130
diagnose_install.sh
Normal file
130
diagnose_install.sh
Normal file
@ -0,0 +1,130 @@
|
||||
#!/bin/bash
|
||||
# MCP Bridge Installation Diagnostic Script
|
||||
# Run this manually in HA terminal to see what's happening
|
||||
|
||||
echo "=== MCP Bridge Installation Diagnostic ==="
|
||||
echo ""
|
||||
|
||||
# 1. Check network connectivity
|
||||
echo "1. Testing Gitea connectivity..."
|
||||
GITEA_URL="${GITEA_URL:-https://www.git.quarantinedstudio.com}"
|
||||
REPO="${REPO:-mvezina/homeassistant-mcp-bridge}"
|
||||
|
||||
echo " Attempting to reach: $GITEA_URL/$REPO"
|
||||
if curl -I "$GITEA_URL/$REPO" 2>&1 | head -n 1; then
|
||||
echo " ✓ Can reach Gitea server"
|
||||
else
|
||||
echo " ✗ Cannot reach Gitea server"
|
||||
echo " Please check:"
|
||||
echo " - Is Gitea running?"
|
||||
echo " - Is the URL correct?"
|
||||
echo " - Can HA reach your Gitea server?"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 2. Check if curl exists
|
||||
echo "2. Checking for curl..."
|
||||
if command -v curl &> /dev/null; then
|
||||
echo " ✓ curl is available: $(which curl)"
|
||||
curl --version | head -n 1
|
||||
else
|
||||
echo " ✗ curl is NOT available"
|
||||
echo " Checking for wget..."
|
||||
if command -v wget &> /dev/null; then
|
||||
echo " ✓ wget is available: $(which wget)"
|
||||
else
|
||||
echo " ✗ Neither curl nor wget available!"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 3. Check config directory
|
||||
echo "3. Checking config directory..."
|
||||
CONFIG_DIR="/config"
|
||||
if [ -d "$CONFIG_DIR" ]; then
|
||||
echo " ✓ /config exists"
|
||||
else
|
||||
echo " ✗ /config does not exist, trying alternate..."
|
||||
CONFIG_DIR="${HOME}/.homeassistant"
|
||||
if [ -d "$CONFIG_DIR" ]; then
|
||||
echo " ✓ Using $CONFIG_DIR"
|
||||
else
|
||||
echo " ✗ Cannot find config directory!"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 4. Check/create custom_components directory
|
||||
echo "4. Checking custom_components directory..."
|
||||
CUSTOM_DIR="$CONFIG_DIR/custom_components"
|
||||
if [ -d "$CUSTOM_DIR" ]; then
|
||||
echo " ✓ $CUSTOM_DIR exists"
|
||||
else
|
||||
echo " ! $CUSTOM_DIR does not exist, creating..."
|
||||
mkdir -p "$CUSTOM_DIR" && echo " ✓ Created successfully" || echo " ✗ Failed to create"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 5. Check write permissions
|
||||
echo "5. Checking write permissions..."
|
||||
TEST_FILE="$CUSTOM_DIR/test_write_$$"
|
||||
if touch "$TEST_FILE" 2>/dev/null; then
|
||||
echo " ✓ Can write to $CUSTOM_DIR"
|
||||
rm "$TEST_FILE"
|
||||
else
|
||||
echo " ✗ Cannot write to $CUSTOM_DIR"
|
||||
echo " Permission issue!"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 6. Try downloading a test file
|
||||
echo "6. Testing file download..."
|
||||
BRANCH="${BRANCH:-main}"
|
||||
TEST_URL="$GITEA_URL/$REPO/raw/branch/$BRANCH/custom_components/mcp_bridge/manifest.json"
|
||||
echo " URL: $TEST_URL"
|
||||
|
||||
TEMP_FILE="/tmp/mcp_test_$$"
|
||||
if curl -fsSL "$TEST_URL" -o "$TEMP_FILE" 2>/dev/null; then
|
||||
echo " ✓ Successfully downloaded test file"
|
||||
echo " File size: $(wc -c < "$TEMP_FILE") bytes"
|
||||
echo " First line: $(head -n 1 "$TEMP_FILE")"
|
||||
rm "$TEMP_FILE"
|
||||
else
|
||||
echo " ✗ Failed to download test file"
|
||||
echo " Possible issues:"
|
||||
echo " - Wrong repository name or path"
|
||||
echo " - Wrong branch name"
|
||||
echo " - File doesn't exist at that path"
|
||||
echo " - Network/firewall blocking"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 7. Check existing installation
|
||||
echo "7. Checking for existing installation..."
|
||||
INSTALL_DIR="$CONFIG_DIR/custom_components/mcp_bridge"
|
||||
if [ -d "$INSTALL_DIR" ]; then
|
||||
echo " ✓ Installation directory exists: $INSTALL_DIR"
|
||||
echo " Files:"
|
||||
ls -lh "$INSTALL_DIR" 2>/dev/null || echo " (empty or cannot list)"
|
||||
|
||||
if [ -f "$INSTALL_DIR/manifest.json" ]; then
|
||||
VERSION=$(grep -oP '"version":\s*"\K[^"]+' "$INSTALL_DIR/manifest.json" 2>/dev/null || echo "unknown")
|
||||
echo " Current version: $VERSION"
|
||||
fi
|
||||
else
|
||||
echo " ! Installation directory does not exist yet"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 8. Provide installation command
|
||||
echo "8. Suggested installation command:"
|
||||
echo ""
|
||||
echo " export GITEA_URL=\"$GITEA_URL\""
|
||||
echo " export REPO=\"$REPO\""
|
||||
echo " export BRANCH=\"$BRANCH\""
|
||||
echo " curl -fsSL \"\$GITEA_URL/\$REPO/raw/branch/\$BRANCH/quick_install.sh\" | bash"
|
||||
echo ""
|
||||
echo "=== Diagnostic Complete ==="
|
||||
echo ""
|
||||
echo "If all checks passed, run the installation command above."
|
||||
echo "If checks failed, fix the issues indicated and try again."
|
||||
36
ha_shell_command.yaml
Normal file
36
ha_shell_command.yaml
Normal file
@ -0,0 +1,36 @@
|
||||
# Add this to your configuration.yaml
|
||||
|
||||
shell_command:
|
||||
# Update MCP Bridge integration from Gitea
|
||||
update_mcp_bridge: >
|
||||
curl -fsSL https://www.git.quarantinedstudio.com/mvezina/homeassistant-mcp-bridge/raw/branch/main/quick_install.sh |
|
||||
GITEA_URL=https://www.git.quarantinedstudio.com
|
||||
REPO=mvezina/homeassistant-mcp-bridge
|
||||
bash
|
||||
|
||||
# Then create an automation or script to call it:
|
||||
|
||||
script:
|
||||
update_mcp_bridge:
|
||||
alias: "Update MCP Bridge Integration"
|
||||
sequence:
|
||||
- service: shell_command.update_mcp_bridge
|
||||
- delay:
|
||||
seconds: 5
|
||||
- service: system_log.write
|
||||
data:
|
||||
message: "MCP Bridge updated. Please restart Home Assistant."
|
||||
level: warning
|
||||
- service: persistent_notification.create
|
||||
data:
|
||||
title: "MCP Bridge Updated"
|
||||
message: "The MCP Bridge integration has been updated. Please restart Home Assistant for changes to take effect."
|
||||
|
||||
# Optional: Add a button to your dashboard
|
||||
button:
|
||||
- type: button
|
||||
name: Update MCP Bridge
|
||||
icon: mdi:download
|
||||
tap_action:
|
||||
action: call-service
|
||||
service: script.update_mcp_bridge
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "MCP Bridge",
|
||||
"content_in_root": false,
|
||||
"render_readme": true,
|
||||
"homeassistant": "2024.1.0"
|
||||
}
|
||||
35
info.md
35
info.md
@ -1,35 +0,0 @@
|
||||
# MCP Bridge
|
||||
|
||||
Bridge integration between Home Assistant and MCP (Model Context Protocol) servers for AI agent control.
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Expose entities to AI agents via voice assistant settings
|
||||
- ✅ Control which scripts are accessible using labels
|
||||
- ✅ Complete entity metadata including area, device class, and attributes
|
||||
- ✅ Secure: Only exposes what you explicitly mark
|
||||
|
||||
## Services
|
||||
|
||||
- `mcp_bridge.get_exposed_entities` - Get all exposed entities
|
||||
- `mcp_bridge.get_exposed_scripts` - Get MCP-accessible scripts
|
||||
- `mcp_bridge.get_entity_metadata` - Get detailed entity info
|
||||
|
||||
## Configuration
|
||||
|
||||
No configuration needed! The integration auto-loads after installation.
|
||||
|
||||
### Exposing Entities
|
||||
|
||||
Go to: **Settings → Voice Assistants → Expose**
|
||||
|
||||
Toggle the entities you want the AI agent to control.
|
||||
|
||||
### Exposing Scripts
|
||||
|
||||
1. Create or edit a script
|
||||
2. Click the settings icon ⚙️
|
||||
3. Add the label: `mcp_accessible`
|
||||
4. Save
|
||||
|
||||
That's it! The script is now accessible to AI agents.
|
||||
0
install_mcp_bridge.sh
Normal file
0
install_mcp_bridge.sh
Normal file
69
quick_install.sh
Normal file
69
quick_install.sh
Normal file
@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# MCP Bridge Quick Installer with Logging
|
||||
# Logs to /config/mcp_bridge_install.log
|
||||
|
||||
# Configuration
|
||||
GITEA_URL="${GITEA_URL:-http://your-gitea.local:3000}"
|
||||
REPO="${REPO:-username/homeassistant-mcp-bridge}"
|
||||
BRANCH="${BRANCH:-main}"
|
||||
BASE_URL="$GITEA_URL/$REPO/raw/branch/$BRANCH/custom_components/mcp_bridge"
|
||||
|
||||
# Logging
|
||||
LOG_FILE="/config/mcp_bridge_install.log"
|
||||
exec 1> >(tee -a "$LOG_FILE")
|
||||
exec 2>&1
|
||||
|
||||
echo "=== MCP Bridge Installation Started at $(date) ==="
|
||||
echo "Configuration:"
|
||||
echo " GITEA_URL: $GITEA_URL"
|
||||
echo " REPO: $REPO"
|
||||
echo " BRANCH: $BRANCH"
|
||||
echo " BASE_URL: $BASE_URL"
|
||||
echo ""
|
||||
|
||||
# Determine config directory
|
||||
CONFIG_DIR="${CONFIG_DIR:-/config}"
|
||||
if [ ! -d "$CONFIG_DIR" ]; then
|
||||
CONFIG_DIR="${HOME}/.homeassistant"
|
||||
echo "Using alternate config dir: $CONFIG_DIR"
|
||||
fi
|
||||
|
||||
DEST="$CONFIG_DIR/custom_components/mcp_bridge"
|
||||
echo "Installation directory: $DEST"
|
||||
echo ""
|
||||
|
||||
# Create directory
|
||||
echo "Creating directory..."
|
||||
mkdir -p "$DEST" || {
|
||||
echo "ERROR: Failed to create directory $DEST"
|
||||
exit 1
|
||||
}
|
||||
echo "Directory created: $DEST"
|
||||
echo ""
|
||||
|
||||
# Download files
|
||||
echo "Downloading files..."
|
||||
cd "$DEST" || exit 1
|
||||
|
||||
for file in __init__.py manifest.json services.yaml; do
|
||||
echo " Downloading $file from $BASE_URL/$file"
|
||||
|
||||
if curl -fsSL "$BASE_URL/$file" -o "$file"; then
|
||||
echo " ✓ $file downloaded successfully ($(wc -c < "$file") bytes)"
|
||||
else
|
||||
echo " ✗ FAILED to download $file"
|
||||
echo " URL attempted: $BASE_URL/$file"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== Installation Summary ==="
|
||||
echo "Files installed in: $DEST"
|
||||
ls -lh "$DEST"
|
||||
echo ""
|
||||
echo "✅ MCP Bridge installed successfully!"
|
||||
echo "⚠️ IMPORTANT: Restart Home Assistant to activate"
|
||||
echo ""
|
||||
echo "Installation completed at $(date)"
|
||||
echo "=== End of Installation Log ==="
|
||||
Loading…
x
Reference in New Issue
Block a user