diff --git a/README.md b/README.md index e02c638..95b181f 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ See [SSE_API.md](docs/SSE_API.md) for complete documentation of the SSE system. - [Configuration](#configuration) - [Development](#development) - [API Reference](#api-reference) + - [MCP Schema](#mcp-schema-endpoint) - [Device Control](#device-control) - [Add-on Management](#add-on-management) - [Package Management](#package-management) @@ -248,6 +249,49 @@ TEST_HASS_TOKEN=test_token # Test token ## API Reference +### MCP Schema Endpoint + +The server exposes an MCP (Model Context Protocol) schema endpoint that describes all available tools and their parameters: + +```http +GET /mcp +``` + +This endpoint returns a JSON schema describing all available tools, their parameters, and documentation resources. The schema follows the MCP specification and can be used by LLM clients to understand the server's capabilities. + +Example response: +```json +{ + "tools": [ + { + "name": "list_devices", + "description": "List all devices connected to Home Assistant", + "parameters": { + "type": "object", + "properties": { + "domain": { + "type": "string", + "enum": ["light", "climate", "alarm_control_panel", ...] + }, + "area": { "type": "string" }, + "floor": { "type": "string" } + } + } + }, + // ... other tools + ], + "prompts": [], + "resources": [ + { + "name": "Home Assistant API", + "url": "https://developers.home-assistant.io/docs/api/rest/" + } + ] +} +``` + +Note: The `/mcp` endpoint is publicly accessible and does not require authentication, as it only provides schema information. + ### Device Control #### Common Entity Controls diff --git a/src/index.ts b/src/index.ts index 60bec99..348ac2c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import { sseManager } from './sse/index.js'; import { ILogger } from "@digital-alchemy/core"; import express from 'express'; import { rateLimiter, securityHeaders, validateRequest, sanitizeInput, errorHandler } from './security/index.js'; +import { MCP_SCHEMA } from './mcp/schema.js'; // Load environment variables based on NODE_ENV const envFile = process.env.NODE_ENV === 'production' @@ -42,6 +43,12 @@ app.use(sanitizeInput); // Initialize LiteMCP const server = new LiteMCP('home-assistant', '0.1.0'); +// MCP schema endpoint - no auth required as it's just the schema +app.get('/mcp', (_req, res) => { + // Return the MCP schema without requiring authentication + res.json(MCP_SCHEMA); +}); + // Health check endpoint app.get('/health', (req, res) => { res.json({ diff --git a/src/mcp/schema.ts b/src/mcp/schema.ts new file mode 100644 index 0000000..beacf78 --- /dev/null +++ b/src/mcp/schema.ts @@ -0,0 +1,118 @@ +import { z } from 'zod'; +import { DomainSchema } from '../schemas.js'; + +export const MCP_SCHEMA = { + tools: [ + { + name: "list_devices", + description: "List all devices connected to Home Assistant", + parameters: { + type: "object", + properties: { + domain: { + type: "string", + enum: [ + "light", + "climate", + "alarm_control_panel", + "cover", + "switch", + "contact", + "media_player", + "fan", + "lock", + "vacuum", + "scene", + "script", + "camera" + ] + }, + area: { type: "string" }, + floor: { type: "string" } + }, + required: [] + } + }, + { + name: "control", + description: "Control Home Assistant entities (lights, climate, etc.)", + parameters: { + type: "object", + properties: { + command: { + type: "string", + enum: [ + "turn_on", + "turn_off", + "toggle", + "open", + "close", + "stop", + "set_position", + "set_tilt_position", + "set_temperature", + "set_hvac_mode", + "set_fan_mode", + "set_humidity" + ] + }, + entity_id: { type: "string" }, + state: { type: "string" }, + brightness: { type: "number" }, + color_temp: { type: "number" }, + rgb_color: { + type: "array", + items: { type: "number" }, + minItems: 3, + maxItems: 3 + }, + position: { type: "number" }, + tilt_position: { type: "number" }, + temperature: { type: "number" }, + target_temp_high: { type: "number" }, + target_temp_low: { type: "number" }, + hvac_mode: { type: "string" }, + fan_mode: { type: "string" }, + humidity: { type: "number" } + }, + required: ["command", "entity_id"] + } + }, + { + name: "subscribe_events", + description: "Subscribe to Home Assistant events via SSE", + parameters: { + type: "object", + properties: { + events: { + type: "array", + items: { type: "string" } + }, + entity_id: { type: "string" }, + domain: { type: "string" } + }, + required: [] + } + }, + { + name: "get_sse_stats", + description: "Get statistics about SSE connections", + parameters: { + type: "object", + properties: {}, + required: [] + } + } + ], + prompts: [], + resources: [ + { + name: "Home Assistant API", + url: "https://developers.home-assistant.io/docs/api/rest/" + }, + { + name: "Home Assistant WebSocket API", + url: "https://developers.home-assistant.io/docs/api/websocket" + } + ] +}; \ No newline at end of file diff --git a/src/security/index.ts b/src/security/index.ts index fd6407e..4f8d422 100644 --- a/src/security/index.ts +++ b/src/security/index.ts @@ -127,8 +127,8 @@ export class TokenManager { // Request validation middleware export function validateRequest(req: Request, res: Response, next: NextFunction) { - // Skip validation for health endpoint - if (req.path === '/health') { + // Skip validation for health and MCP schema endpoints + if (req.path === '/health' || req.path === '/mcp') { return next(); }