Add MCP Schema Endpoint for API Discovery
- Implemented `/mcp` endpoint to expose server capabilities - Created `src/mcp/schema.ts` with comprehensive tool and resource definitions - Updated README.md with MCP Schema documentation - Modified security middleware to allow unauthenticated access to schema endpoint - Included detailed tool descriptions for list_devices, control, and event subscription
This commit is contained in:
44
README.md
44
README.md
@@ -54,6 +54,7 @@ See [SSE_API.md](docs/SSE_API.md) for complete documentation of the SSE system.
|
|||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
- [Development](#development)
|
- [Development](#development)
|
||||||
- [API Reference](#api-reference)
|
- [API Reference](#api-reference)
|
||||||
|
- [MCP Schema](#mcp-schema-endpoint)
|
||||||
- [Device Control](#device-control)
|
- [Device Control](#device-control)
|
||||||
- [Add-on Management](#add-on-management)
|
- [Add-on Management](#add-on-management)
|
||||||
- [Package Management](#package-management)
|
- [Package Management](#package-management)
|
||||||
@@ -248,6 +249,49 @@ TEST_HASS_TOKEN=test_token # Test token
|
|||||||
|
|
||||||
## API Reference
|
## 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
|
### Device Control
|
||||||
|
|
||||||
#### Common Entity Controls
|
#### Common Entity Controls
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { sseManager } from './sse/index.js';
|
|||||||
import { ILogger } from "@digital-alchemy/core";
|
import { ILogger } from "@digital-alchemy/core";
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { rateLimiter, securityHeaders, validateRequest, sanitizeInput, errorHandler } from './security/index.js';
|
import { rateLimiter, securityHeaders, validateRequest, sanitizeInput, errorHandler } from './security/index.js';
|
||||||
|
import { MCP_SCHEMA } from './mcp/schema.js';
|
||||||
|
|
||||||
// Load environment variables based on NODE_ENV
|
// Load environment variables based on NODE_ENV
|
||||||
const envFile = process.env.NODE_ENV === 'production'
|
const envFile = process.env.NODE_ENV === 'production'
|
||||||
@@ -42,6 +43,12 @@ app.use(sanitizeInput);
|
|||||||
// Initialize LiteMCP
|
// Initialize LiteMCP
|
||||||
const server = new LiteMCP('home-assistant', '0.1.0');
|
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
|
// Health check endpoint
|
||||||
app.get('/health', (req, res) => {
|
app.get('/health', (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
|
|||||||
118
src/mcp/schema.ts
Normal file
118
src/mcp/schema.ts
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -127,8 +127,8 @@ export class TokenManager {
|
|||||||
|
|
||||||
// Request validation middleware
|
// Request validation middleware
|
||||||
export function validateRequest(req: Request, res: Response, next: NextFunction) {
|
export function validateRequest(req: Request, res: Response, next: NextFunction) {
|
||||||
// Skip validation for health endpoint
|
// Skip validation for health and MCP schema endpoints
|
||||||
if (req.path === '/health') {
|
if (req.path === '/health' || req.path === '/mcp') {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user