Initial commit: Home Assistant MCP Server
This commit is contained in:
3
.env.example
Normal file
3
.env.example
Normal file
@@ -0,0 +1,3 @@
|
||||
# Home Assistant Configuration
|
||||
HA_URL=http://localhost:8123
|
||||
HA_TOKEN=your_long_lived_access_token_here
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.env
|
||||
*.log
|
||||
98
README.md
Normal file
98
README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Home Assistant MCP Server
|
||||
|
||||
An MCP (Model Context Protocol) server that enables LLMs to interact with Home Assistant through its REST API.
|
||||
|
||||
## Features
|
||||
|
||||
- **get_states** - Get entity states (all or specific)
|
||||
- **call_service** - Call Home Assistant services (turn on/off lights, switches, etc.)
|
||||
- **get_history** - Get historical state data for entities
|
||||
- **get_config** - Get Home Assistant configuration
|
||||
- **get_services** - List available services
|
||||
- **render_template** - Render Jinja2 templates
|
||||
- **get_logbook** - Get logbook entries
|
||||
- **fire_event** - Fire custom events
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Generate a Long-Lived Access Token
|
||||
|
||||
1. Log in to your Home Assistant instance
|
||||
2. Go to your Profile (click on your name in the sidebar)
|
||||
3. Scroll down to "Long-Lived Access Tokens"
|
||||
4. Click "Create Token"
|
||||
5. Give it a name (e.g., "MCP Server")
|
||||
6. Copy the token immediately (it won't be shown again!)
|
||||
|
||||
### 2. Configure Environment
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Edit `.env` with your Home Assistant URL and token:
|
||||
|
||||
```
|
||||
HA_URL=http://your-home-assistant-ip:8123
|
||||
HA_TOKEN=your_long_lived_access_token
|
||||
```
|
||||
|
||||
### 3. Install and Build
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### With Claude Desktop
|
||||
|
||||
Add to your Claude Desktop configuration (`~/.config/Claude/claude_desktop_config.json` on Linux):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"home-assistant": {
|
||||
"command": "node",
|
||||
"args": ["/path/to/home-assistant-mcp/dist/index.js"],
|
||||
"env": {
|
||||
"HA_URL": "http://your-home-assistant-ip:8123",
|
||||
"HA_TOKEN": "your_long_lived_access_token"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Other MCP Clients
|
||||
|
||||
Run the server:
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
The server communicates via stdio using the MCP protocol.
|
||||
|
||||
## Example Commands
|
||||
|
||||
Once connected, you can ask the LLM things like:
|
||||
|
||||
- "What's the current temperature in the living room?"
|
||||
- "Turn on the kitchen lights"
|
||||
- "Show me what happened in the last hour"
|
||||
- "What's the status of all my lights?"
|
||||
- "Set the thermostat to 22 degrees"
|
||||
|
||||
## Development
|
||||
|
||||
Watch mode for TypeScript:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
1178
package-lock.json
generated
Normal file
1178
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
package.json
Normal file
31
package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "home-assistant-mcp",
|
||||
"version": "1.0.0",
|
||||
"description": "MCP Server for Home Assistant integration",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
"home-assistant-mcp": "./dist/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"dev": "tsc --watch"
|
||||
},
|
||||
"keywords": [
|
||||
"mcp",
|
||||
"home-assistant",
|
||||
"llm",
|
||||
"ai"
|
||||
],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.0.0",
|
||||
"dotenv": "^16.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.0",
|
||||
"typescript": "^5.3.0"
|
||||
}
|
||||
}
|
||||
4
run_server.sh
Executable file
4
run_server.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
export HA_URL="http://10.8.0.17:8123"
|
||||
export HA_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3MjkxZDFhNmQ1ZDQ0MGI1YmQ5ODgwYTZlZTZjYmIxYiIsImlhdCI6MTc2OTkxNzcyNSwiZXhwIjoyMDg1Mjc3NzI1fQ.Bpqs8Tr7Nxle377HbIP5u8fQZnSL5rQjYxBYaeEJMuw"
|
||||
exec /usr/bin/node /home/wartana/myApp/home-assistant-mcp/dist/index.js
|
||||
220
src/client.ts
Normal file
220
src/client.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* Home Assistant API Client
|
||||
* Provides HTTP client for interacting with Home Assistant REST API
|
||||
*/
|
||||
|
||||
export interface HAState {
|
||||
entity_id: string;
|
||||
state: string;
|
||||
attributes: Record<string, unknown>;
|
||||
last_changed: string;
|
||||
last_updated: string;
|
||||
}
|
||||
|
||||
export interface HAConfig {
|
||||
components: string[];
|
||||
config_dir: string;
|
||||
elevation: number;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
location_name: string;
|
||||
time_zone: string;
|
||||
unit_system: {
|
||||
length: string;
|
||||
mass: string;
|
||||
temperature: string;
|
||||
volume: string;
|
||||
};
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface HAService {
|
||||
domain: string;
|
||||
services: Record<string, {
|
||||
description?: string;
|
||||
fields?: Record<string, unknown>;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface HAHistoryEntry {
|
||||
entity_id: string;
|
||||
state: string;
|
||||
attributes?: Record<string, unknown>;
|
||||
last_changed: string;
|
||||
last_updated?: string;
|
||||
}
|
||||
|
||||
export interface HALogbookEntry {
|
||||
context_user_id: string | null;
|
||||
domain: string;
|
||||
entity_id: string;
|
||||
message: string;
|
||||
name: string;
|
||||
when: string;
|
||||
}
|
||||
|
||||
export class HomeAssistantClient {
|
||||
private baseUrl: string;
|
||||
private token: string;
|
||||
|
||||
constructor(baseUrl: string, token: string) {
|
||||
// Remove trailing slash if present
|
||||
this.baseUrl = baseUrl.replace(/\/$/, '');
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
private async request<T>(
|
||||
endpoint: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<T> {
|
||||
const url = `${this.baseUrl}${endpoint}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token}`,
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Home Assistant API error (${response.status}): ${errorText}`);
|
||||
}
|
||||
|
||||
// Some endpoints return plain text
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (contentType?.includes('application/json')) {
|
||||
return response.json() as Promise<T>;
|
||||
}
|
||||
|
||||
return response.text() as unknown as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the API is running
|
||||
*/
|
||||
async checkApi(): Promise<{ message: string }> {
|
||||
return this.request<{ message: string }>('/api/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Home Assistant configuration
|
||||
*/
|
||||
async getConfig(): Promise<HAConfig> {
|
||||
return this.request<HAConfig>('/api/config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all entity states
|
||||
*/
|
||||
async getStates(): Promise<HAState[]> {
|
||||
return this.request<HAState[]>('/api/states');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get state of a specific entity
|
||||
*/
|
||||
async getState(entityId: string): Promise<HAState> {
|
||||
return this.request<HAState>(`/api/states/${entityId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available services
|
||||
*/
|
||||
async getServices(): Promise<HAService[]> {
|
||||
return this.request<HAService[]>('/api/services');
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a service
|
||||
*/
|
||||
async callService(
|
||||
domain: string,
|
||||
service: string,
|
||||
data?: Record<string, unknown>,
|
||||
returnResponse = false
|
||||
): Promise<HAState[] | Record<string, unknown>> {
|
||||
const endpoint = `/api/services/${domain}/${service}${returnResponse ? '?return_response' : ''}`;
|
||||
return this.request<HAState[] | Record<string, unknown>>(endpoint, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entity history
|
||||
*/
|
||||
async getHistory(
|
||||
entityId: string,
|
||||
startTime?: string,
|
||||
endTime?: string,
|
||||
minimalResponse = true
|
||||
): Promise<HAHistoryEntry[][]> {
|
||||
const params = new URLSearchParams();
|
||||
params.set('filter_entity_id', entityId);
|
||||
if (minimalResponse) {
|
||||
params.set('minimal_response', '');
|
||||
}
|
||||
if (endTime) {
|
||||
params.set('end_time', endTime);
|
||||
}
|
||||
|
||||
const timestamp = startTime || '';
|
||||
const endpoint = `/api/history/period/${timestamp}?${params.toString()}`;
|
||||
return this.request<HAHistoryEntry[][]>(endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logbook entries
|
||||
*/
|
||||
async getLogbook(
|
||||
entityId?: string,
|
||||
startTime?: string,
|
||||
endTime?: string
|
||||
): Promise<HALogbookEntry[]> {
|
||||
const params = new URLSearchParams();
|
||||
if (entityId) {
|
||||
params.set('entity', entityId);
|
||||
}
|
||||
if (endTime) {
|
||||
params.set('end_time', endTime);
|
||||
}
|
||||
|
||||
const timestamp = startTime || '';
|
||||
const queryString = params.toString();
|
||||
const endpoint = `/api/logbook/${timestamp}${queryString ? '?' + queryString : ''}`;
|
||||
return this.request<HALogbookEntry[]>(endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a template
|
||||
*/
|
||||
async renderTemplate(template: string): Promise<string> {
|
||||
return this.request<string>('/api/template', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ template }),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get error log
|
||||
*/
|
||||
async getErrorLog(): Promise<string> {
|
||||
return this.request<string>('/api/error_log');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire an event
|
||||
*/
|
||||
async fireEvent(
|
||||
eventType: string,
|
||||
eventData?: Record<string, unknown>
|
||||
): Promise<{ message: string }> {
|
||||
return this.request<{ message: string }>(`/api/events/${eventType}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(eventData || {}),
|
||||
});
|
||||
}
|
||||
}
|
||||
356
src/index.ts
Normal file
356
src/index.ts
Normal file
@@ -0,0 +1,356 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Home Assistant MCP Server
|
||||
* Provides tools for LLMs to interact with Home Assistant
|
||||
*/
|
||||
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import {
|
||||
CallToolRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
Tool,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import { config } from 'dotenv';
|
||||
import { HomeAssistantClient } from './client.js';
|
||||
|
||||
// Load environment variables
|
||||
config();
|
||||
|
||||
const HA_URL = process.env.HA_URL || 'http://localhost:8123';
|
||||
const HA_TOKEN = process.env.HA_TOKEN || '';
|
||||
|
||||
if (!HA_TOKEN) {
|
||||
console.error('Error: HA_TOKEN environment variable is required');
|
||||
console.error('Please set HA_TOKEN to your Home Assistant Long-Lived Access Token');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Initialize Home Assistant client
|
||||
const haClient = new HomeAssistantClient(HA_URL, HA_TOKEN);
|
||||
|
||||
// Define available tools
|
||||
const tools: Tool[] = [
|
||||
{
|
||||
name: 'get_states',
|
||||
description: 'Get the current state of entities in Home Assistant. Returns all entities if no entity_id is specified, or a specific entity if entity_id is provided.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
entity_id: {
|
||||
type: 'string',
|
||||
description: 'Optional. The entity ID to get state for (e.g., "light.living_room", "sensor.temperature"). If not provided, returns all entities.',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'call_service',
|
||||
description: 'Call a Home Assistant service to control devices. Common services include: light.turn_on, light.turn_off, switch.turn_on, switch.turn_off, climate.set_temperature, etc.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
domain: {
|
||||
type: 'string',
|
||||
description: 'The service domain (e.g., "light", "switch", "climate", "automation")',
|
||||
},
|
||||
service: {
|
||||
type: 'string',
|
||||
description: 'The service to call (e.g., "turn_on", "turn_off", "toggle", "set_temperature")',
|
||||
},
|
||||
data: {
|
||||
type: 'object',
|
||||
description: 'Optional service data. Usually includes "entity_id" and service-specific parameters.',
|
||||
},
|
||||
},
|
||||
required: ['domain', 'service'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_history',
|
||||
description: 'Get the state history of an entity over a time period. Useful for checking historical values of sensors, when devices were turned on/off, etc.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
entity_id: {
|
||||
type: 'string',
|
||||
description: 'The entity ID to get history for (e.g., "sensor.temperature")',
|
||||
},
|
||||
start_time: {
|
||||
type: 'string',
|
||||
description: 'Optional. Start time in ISO format (e.g., "2024-01-01T00:00:00"). Defaults to 24 hours ago.',
|
||||
},
|
||||
end_time: {
|
||||
type: 'string',
|
||||
description: 'Optional. End time in ISO format. Defaults to now.',
|
||||
},
|
||||
},
|
||||
required: ['entity_id'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_config',
|
||||
description: 'Get Home Assistant configuration including version, location, timezone, and installed components.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_services',
|
||||
description: 'Get a list of all available services in Home Assistant. Optionally filter by domain.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
domain: {
|
||||
type: 'string',
|
||||
description: 'Optional. Filter services by domain (e.g., "light", "switch")',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'render_template',
|
||||
description: 'Render a Home Assistant Jinja2 template. Useful for getting computed values or formatted information.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
template: {
|
||||
type: 'string',
|
||||
description: 'The Jinja2 template to render. Example: "{{ states(\'sensor.temperature\') }} °C"',
|
||||
},
|
||||
},
|
||||
required: ['template'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'get_logbook',
|
||||
description: 'Get logbook entries showing what happened in Home Assistant. Shows events like state changes, automations triggered, etc.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
entity_id: {
|
||||
type: 'string',
|
||||
description: 'Optional. Filter logbook entries by entity ID.',
|
||||
},
|
||||
start_time: {
|
||||
type: 'string',
|
||||
description: 'Optional. Start time in ISO format. Defaults to 24 hours ago.',
|
||||
},
|
||||
end_time: {
|
||||
type: 'string',
|
||||
description: 'Optional. End time in ISO format. Defaults to now.',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'fire_event',
|
||||
description: 'Fire a custom event in Home Assistant. Useful for triggering automations or custom integrations.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
event_type: {
|
||||
type: 'string',
|
||||
description: 'The event type to fire (e.g., "custom_event", "my_automation_trigger")',
|
||||
},
|
||||
event_data: {
|
||||
type: 'object',
|
||||
description: 'Optional. Data to include with the event.',
|
||||
},
|
||||
},
|
||||
required: ['event_type'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// Create server
|
||||
const server = new Server(
|
||||
{
|
||||
name: 'home-assistant-mcp',
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Handle list tools request
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return { tools };
|
||||
});
|
||||
|
||||
// Handle tool calls
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
|
||||
try {
|
||||
switch (name) {
|
||||
case 'get_states': {
|
||||
const entityId = args?.entity_id as string | undefined;
|
||||
if (entityId) {
|
||||
const state = await haClient.getState(entityId);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(state, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
const states = await haClient.getStates();
|
||||
// Return summary for all states to avoid overwhelming response
|
||||
const summary = states.map((s) => ({
|
||||
entity_id: s.entity_id,
|
||||
state: s.state,
|
||||
friendly_name: s.attributes.friendly_name,
|
||||
}));
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(summary, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
case 'call_service': {
|
||||
const domain = args?.domain as string;
|
||||
const service = args?.service as string;
|
||||
const data = args?.data as Record<string, unknown> | undefined;
|
||||
|
||||
const result = await haClient.callService(domain, service, data);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Service ${domain}.${service} called successfully.\n\nChanged states:\n${JSON.stringify(result, null, 2)}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'get_history': {
|
||||
const entityId = args?.entity_id as string;
|
||||
const startTime = args?.start_time as string | undefined;
|
||||
const endTime = args?.end_time as string | undefined;
|
||||
|
||||
const history = await haClient.getHistory(entityId, startTime, endTime);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(history, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'get_config': {
|
||||
const config = await haClient.getConfig();
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(config, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'get_services': {
|
||||
const domain = args?.domain as string | undefined;
|
||||
let services = await haClient.getServices();
|
||||
|
||||
if (domain) {
|
||||
services = services.filter((s) => s.domain === domain);
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(services, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'render_template': {
|
||||
const template = args?.template as string;
|
||||
const result = await haClient.renderTemplate(template);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: result,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'get_logbook': {
|
||||
const entityId = args?.entity_id as string | undefined;
|
||||
const startTime = args?.start_time as string | undefined;
|
||||
const endTime = args?.end_time as string | undefined;
|
||||
|
||||
const logbook = await haClient.getLogbook(entityId, startTime, endTime);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(logbook, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
case 'fire_event': {
|
||||
const eventType = args?.event_type as string;
|
||||
const eventData = args?.event_data as Record<string, unknown> | undefined;
|
||||
|
||||
const result = await haClient.fireEvent(eventType, eventData);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: result.message,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown tool: ${name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Error: ${errorMessage}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Start server
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error('Home Assistant MCP Server running on stdio');
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": [
|
||||
"ES2022"
|
||||
],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user