Skip to main content
Glama

HomeAssistant MCP

all-tools.test.tsโ€ข45.3 kB
import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"; import { tools } from "../../src/tools/index.js"; import { TEST_CONFIG, createMockResponse, } from "../utils/test-utils.js"; type TestResult = { success: boolean; error?: string; message?: string; [key: string]: unknown }; function parseResult(result: unknown): TestResult { if (typeof result === "string") { return JSON.parse(result) as TestResult; } return result as TestResult; } describe("Comprehensive Tool Suite Tests", () => { let mocks: { mockFetch: ReturnType<typeof mock> }; beforeEach(async () => { // Setup mock fetch mocks = { mockFetch: mock(() => Promise.resolve(createMockResponse({}))), }; globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; await Promise.resolve(); }); afterEach(() => { // Reset mocks globalThis.fetch = mock(() => Promise.resolve(createMockResponse({}))) as unknown as typeof fetch; }); describe("Tool Registry", () => { test("should have all 19 tools registered", () => { expect(tools.length).toBe(19); }); test("should have unique tool names", () => { const names = tools.map((t) => t.name); const uniqueNames = new Set(names); expect(names.length).toBe(uniqueNames.size); }); test("should have all tools with required properties", () => { tools.forEach((tool) => { expect(tool.name).toBeDefined(); expect(typeof tool.name).toBe("string"); expect(tool.description).toBeDefined(); expect(typeof tool.description).toBe("string"); expect(tool.parameters).toBeDefined(); expect(tool.execute).toBeDefined(); expect(typeof tool.execute).toBe("function"); }); }); test("should list all available tools", () => { const toolNames = tools.map((t) => t.name).sort(); const expectedTools = [ "addon", "alarm_control", "automation", "automation_config", "climate_control", "control", "cover_control", "fan_control", "get_history", "get_sse_stats", "lights_control", "list_devices", "lock_control", "media_player_control", "notify", "package", "scene", "subscribe_events", "vacuum_control", ].sort(); expect(toolNames).toEqual(expectedTools); }); }); describe("Control Tool", () => { test("should execute turn_on command", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const controlTool = tools.find((t) => t.name === "control"); expect(controlTool).toBeDefined(); if (!controlTool) throw new Error("control tool not found"); const result: unknown = await controlTool.execute({ command: "turn_on", entity_id: "light.living_room", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should handle missing entity_id and area_id", async () => { const controlTool = tools.find((t) => t.name === "control"); expect(controlTool).toBeDefined(); if (!controlTool) throw new Error("control tool not found"); const result: unknown = await controlTool.execute({ command: "turn_on", }); const parsed = parseResult(result); expect(parsed.success).toBe(false); expect(parsed.error).toContain("entity_id or area_id"); }); test("should reject unsupported domains", async () => { const controlTool = tools.find((t) => t.name === "control"); expect(controlTool).toBeDefined(); if (!controlTool) throw new Error("control tool not found"); const result: unknown = await controlTool.execute({ command: "turn_on", entity_id: "unsupported.device", }); const parsed = parseResult(result); expect(parsed.success).toBe(false); expect(parsed.error).toContain("Unsupported domain"); }); test("should handle temperature control for climate devices", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const controlTool = tools.find((t) => t.name === "control"); expect(controlTool).toBeDefined(); if (!controlTool) throw new Error("control tool not found"); const result: unknown = await controlTool.execute({ command: "set_temperature", entity_id: "climate.bedroom", temperature: 22, hvac_mode: "heat", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("History Tool", () => { test("should fetch entity history", async () => { const mockHistory = [ { entity_id: "light.living_room", state: "on", last_changed: "2024-01-01T10:00:00.000Z", last_updated: "2024-01-01T10:00:00.000Z", }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockHistory)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const historyTool = tools.find((t) => t.name === "get_history"); expect(historyTool).toBeDefined(); if (!historyTool) throw new Error("get_history tool not found"); const result: unknown = await historyTool.execute({ entity_id: "light.living_room", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); expect(parsed.history).toBeDefined(); }); test("should handle history fetch errors", async () => { mocks.mockFetch = mock(() => Promise.resolve( new Response(null, { status: 500, statusText: "Server Error" }) ) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const historyTool = tools.find((t) => t.name === "get_history"); expect(historyTool).toBeDefined(); if (!historyTool) throw new Error("get_history tool not found"); const result: unknown = await historyTool.execute({ entity_id: "light.living_room", }); const parsed = parseResult(result); expect(parsed.success).toBe(false); }); test("should support custom time ranges", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse([]))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const historyTool = tools.find((t) => t.name === "get_history"); expect(historyTool).toBeDefined(); if (!historyTool) throw new Error("get_history tool not found"); const result: unknown = await historyTool.execute({ entity_id: "light.living_room", start_time: "2024-01-01T00:00:00.000Z", end_time: "2024-01-02T00:00:00.000Z", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Addon Tool", () => { test("should list available add-ons", async () => { const mockAddons = { data: { addons: [ { name: "Test Add-on", slug: "test-addon", description: "A test add-on", version: "1.0.0", installed: false, available: true, state: "stopped", }, ], }, }; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockAddons)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const addonTool = tools.find((t) => t.name === "addon"); expect(addonTool).toBeDefined(); if (!addonTool) throw new Error("addon tool not found"); const result: unknown = await addonTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); expect(parsed.addons).toBeDefined(); }); test("should require slug for non-list actions", async () => { const addonTool = tools.find((t) => t.name === "addon"); expect(addonTool).toBeDefined(); if (!addonTool) throw new Error("addon tool not found"); const result: unknown = await addonTool.execute({ action: "info" }); const parsed = parseResult(result); expect(parsed.success).toBe(false); expect(parsed.error).toContain("slug is required"); }); test("should install add-on with optional version", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({ data: {} })) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const addonTool = tools.find((t) => t.name === "addon"); expect(addonTool).toBeDefined(); if (!addonTool) throw new Error("addon tool not found"); const result: unknown = await addonTool.execute({ action: "install", slug: "test-addon", version: "1.0.0", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Package Tool (HACS)", () => { test("should list packages by category", async () => { const mockPackages = { repositories: [ { id: 1, name: "test-pkg", category: "integration" }, ], }; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockPackages)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const packageTool = tools.find((t) => t.name === "package"); expect(packageTool).toBeDefined(); if (!packageTool) throw new Error("package tool not found"); const result: unknown = await packageTool.execute({ action: "list", category: "integration", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should require repository for install action", async () => { const packageTool = tools.find((t) => t.name === "package"); expect(packageTool).toBeDefined(); if (!packageTool) throw new Error("package tool not found"); const result: unknown = await packageTool.execute({ action: "install", category: "integration", }); const parsed = parseResult(result); expect(parsed.success).toBe(false); }); test("should install package with version", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const packageTool = tools.find((t) => t.name === "package"); expect(packageTool).toBeDefined(); if (!packageTool) throw new Error("package tool not found"); const result: unknown = await packageTool.execute({ action: "install", category: "integration", repository: "test/repo", version: "1.0.0", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Automation Config Tool", () => { test("should require config for create action", async () => { const automationConfigTool = tools.find( (t) => t.name === "automation_config" ); expect(automationConfigTool).toBeDefined(); if (!automationConfigTool) throw new Error("automation_config tool not found"); const result: unknown = await automationConfigTool.execute({ action: "create", }); const parsed = parseResult(result); expect(parsed.success).toBe(false); }); test("should create automation with valid config", async () => { const mockConfig = { alias: "Test Automation", trigger: [{ platform: "state", entity_id: "sensor.test" }], action: [{ service: "light.turn_on" }], }; mocks.mockFetch = mock(() => Promise.resolve( createMockResponse({ automation_id: "automation.test" }) ) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const automationConfigTool = tools.find( (t) => t.name === "automation_config" ); expect(automationConfigTool).toBeDefined(); if (!automationConfigTool) throw new Error("automation_config tool not found"); const result: unknown = await automationConfigTool.execute({ action: "create", config: mockConfig, }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Subscribe Events Tool", () => { test("should require authentication token", async () => { const subscribeTool = tools.find( (t) => t.name === "subscribe_events" ); expect(subscribeTool).toBeDefined(); if (!subscribeTool) throw new Error("subscribe_events tool not found"); const result: unknown = await subscribeTool.execute({ token: "invalid_token", }); const parsed = parseResult(result); // May require actual SSE setup; adjust based on implementation expect(parsed).toBeDefined(); }); test("should support subscribing to specific events", async () => { const subscribeTool = tools.find( (t) => t.name === "subscribe_events" ); expect(subscribeTool).toBeDefined(); if (!subscribeTool) throw new Error("subscribe_events tool not found"); const result: unknown = await subscribeTool.execute({ token: TEST_CONFIG.HASS_TOKEN, events: ["state_changed", "service_called"], }); const parsed = parseResult(result); expect(parsed).toBeDefined(); }); test("should support subscribing to entity", async () => { const subscribeTool = tools.find( (t) => t.name === "subscribe_events" ); expect(subscribeTool).toBeDefined(); if (!subscribeTool) throw new Error("subscribe_events tool not found"); const result: unknown = await subscribeTool.execute({ token: TEST_CONFIG.HASS_TOKEN, entity_id: "light.living_room", }); const parsed = parseResult(result); expect(parsed).toBeDefined(); }); }); describe("SSE Stats Tool", () => { test("should require authentication token", async () => { const sseStatsTool = tools.find((t) => t.name === "get_sse_stats"); expect(sseStatsTool).toBeDefined(); if (!sseStatsTool) throw new Error("get_sse_stats tool not found"); const result: unknown = await sseStatsTool.execute({ token: "invalid_token", }); const parsed = parseResult(result); expect(parsed.success).toBe(false); }); test("should return statistics with valid token", async () => { const sseStatsTool = tools.find((t) => t.name === "get_sse_stats"); expect(sseStatsTool).toBeDefined(); if (!sseStatsTool) throw new Error("get_sse_stats tool not found"); const result: unknown = await sseStatsTool.execute({ token: TEST_CONFIG.HASS_TOKEN, }); const parsed = parseResult(result); expect(parsed.success).toBe(true); expect(parsed.statistics).toBeDefined(); }); }); describe("Lights Control Tool", () => { test("should turn on light", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const lightsTool = tools.find((t) => t.name === "lights_control"); expect(lightsTool).toBeDefined(); if (!lightsTool) throw new Error("lights tool not found"); const result: unknown = await lightsTool.execute({ action: "turn_on", entity_id: "light.living_room", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should turn off light", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const lightsTool = tools.find((t) => t.name === "lights_control"); expect(lightsTool).toBeDefined(); if (!lightsTool) throw new Error("lights tool not found"); const result: unknown = await lightsTool.execute({ action: "turn_off", entity_id: "light.living_room", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should list all lights", async () => { const mockLights = [ { entity_id: "light.living_room", state: "on", attributes: { brightness: 255 }, }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockLights)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const lightsTool = tools.find((t) => t.name === "lights_control"); expect(lightsTool).toBeDefined(); if (!lightsTool) throw new Error("lights tool not found"); const result: unknown = await lightsTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should set brightness", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const lightsTool = tools.find((t) => t.name === "lights_control"); expect(lightsTool).toBeDefined(); if (!lightsTool) throw new Error("lights tool not found"); const result: unknown = await lightsTool.execute({ action: "turn_on", entity_id: "light.living_room", brightness: 128, }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Climate Control Tool", () => { test("should set temperature", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const climateTool = tools.find((t) => t.name === "climate_control"); expect(climateTool).toBeDefined(); if (!climateTool) throw new Error("climate tool not found"); const result: unknown = await climateTool.execute({ action: "set_temperature", entity_id: "climate.bedroom", temperature: 22, }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should set HVAC mode", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const climateTool = tools.find((t) => t.name === "climate_control"); expect(climateTool).toBeDefined(); if (!climateTool) throw new Error("climate tool not found"); const result: unknown = await climateTool.execute({ action: "set_hvac_mode", entity_id: "climate.bedroom", hvac_mode: "heat", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should list climate devices", async () => { const mockClimate = [ { entity_id: "climate.bedroom", state: "heat", attributes: { temperature: 22 }, }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockClimate)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const climateTool = tools.find((t) => t.name === "climate_control"); expect(climateTool).toBeDefined(); if (!climateTool) throw new Error("climate tool not found"); const result: unknown = await climateTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Cover Control Tool", () => { test("should open cover", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const coverTool = tools.find((t) => t.name === "cover_control"); expect(coverTool).toBeDefined(); if (!coverTool) throw new Error("cover tool not found"); const result: unknown = await coverTool.execute({ action: "open_cover", entity_id: "cover.blinds", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should close cover", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const coverTool = tools.find((t) => t.name === "cover_control"); expect(coverTool).toBeDefined(); if (!coverTool) throw new Error("cover tool not found"); const result: unknown = await coverTool.execute({ action: "close_cover", entity_id: "cover.blinds", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should set position", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const coverTool = tools.find((t) => t.name === "cover_control"); expect(coverTool).toBeDefined(); if (!coverTool) throw new Error("cover tool not found"); const result: unknown = await coverTool.execute({ action: "set_cover_position", entity_id: "cover.blinds", position: 50, }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Fan Control Tool", () => { test("should turn on fan", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const fanTool = tools.find((t) => t.name === "fan_control"); expect(fanTool).toBeDefined(); if (!fanTool) throw new Error("fan tool not found"); const result: unknown = await fanTool.execute({ action: "turn_on", entity_id: "fan.bedroom", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should set fan speed", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const fanTool = tools.find((t) => t.name === "fan_control"); expect(fanTool).toBeDefined(); if (!fanTool) throw new Error("fan tool not found"); const result: unknown = await fanTool.execute({ action: "set_percentage", entity_id: "fan.bedroom", percentage: 75, }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should list fans", async () => { const mockFans = [ { entity_id: "fan.bedroom", state: "on", attributes: { percentage: 75 }, }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockFans)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const fanTool = tools.find((t) => t.name === "fan_control"); expect(fanTool).toBeDefined(); if (!fanTool) throw new Error("fan tool not found"); const result: unknown = await fanTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Lock Control Tool", () => { test("should lock door", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const lockTool = tools.find((t) => t.name === "lock_control"); expect(lockTool).toBeDefined(); if (!lockTool) throw new Error("lock tool not found"); const result: unknown = await lockTool.execute({ action: "lock", entity_id: "lock.front_door", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should unlock door", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const lockTool = tools.find((t) => t.name === "lock_control"); expect(lockTool).toBeDefined(); if (!lockTool) throw new Error("lock tool not found"); const result: unknown = await lockTool.execute({ action: "unlock", entity_id: "lock.front_door", code: "1234", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should list locks", async () => { const mockLocks = [ { entity_id: "lock.front_door", state: "locked", attributes: {}, }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockLocks)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const lockTool = tools.find((t) => t.name === "lock_control"); expect(lockTool).toBeDefined(); if (!lockTool) throw new Error("lock tool not found"); const result: unknown = await lockTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Vacuum Control Tool", () => { test("should start vacuum", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const vacuumTool = tools.find((t) => t.name === "vacuum_control"); expect(vacuumTool).toBeDefined(); if (!vacuumTool) throw new Error("vacuum tool not found"); const result: unknown = await vacuumTool.execute({ action: "start", entity_id: "vacuum.robot", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should return to dock", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const vacuumTool = tools.find((t) => t.name === "vacuum_control"); expect(vacuumTool).toBeDefined(); if (!vacuumTool) throw new Error("vacuum tool not found"); const result: unknown = await vacuumTool.execute({ action: "return_to_base", entity_id: "vacuum.robot", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should list vacuums", async () => { const mockVacuums = [ { entity_id: "vacuum.robot", state: "cleaning", attributes: { battery: 80 }, }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockVacuums)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const vacuumTool = tools.find((t) => t.name === "vacuum_control"); expect(vacuumTool).toBeDefined(); if (!vacuumTool) throw new Error("vacuum tool not found"); const result: unknown = await vacuumTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Media Player Control Tool", () => { test("should turn on media player", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const mediaPlayerTool = tools.find( (t) => t.name === "media_player_control" ); expect(mediaPlayerTool).toBeDefined(); if (!mediaPlayerTool) throw new Error("media_player tool not found"); const result: unknown = await mediaPlayerTool.execute({ action: "turn_on", entity_id: "media_player.living_room", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should pause media", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const mediaPlayerTool = tools.find( (t) => t.name === "media_player_control" ); expect(mediaPlayerTool).toBeDefined(); if (!mediaPlayerTool) throw new Error("media_player tool not found"); const result: unknown = await mediaPlayerTool.execute({ action: "media_pause", entity_id: "media_player.living_room", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should list media players", async () => { const mockMediaPlayers = [ { entity_id: "media_player.living_room", state: "playing", attributes: { media_title: "Test Song" }, }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockMediaPlayers)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const mediaPlayerTool = tools.find( (t) => t.name === "media_player_control" ); expect(mediaPlayerTool).toBeDefined(); if (!mediaPlayerTool) throw new Error("media_player tool not found"); const result: unknown = await mediaPlayerTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Alarm Control Tool", () => { test("should arm alarm", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const alarmTool = tools.find((t) => t.name === "alarm_control"); expect(alarmTool).toBeDefined(); if (!alarmTool) throw new Error("alarm_control tool not found"); const result: unknown = await alarmTool.execute({ action: "alarm_arm_away", entity_id: "alarm_control_panel.home", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should disarm alarm", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const alarmTool = tools.find((t) => t.name === "alarm_control"); expect(alarmTool).toBeDefined(); if (!alarmTool) throw new Error("alarm_control tool not found"); const result: unknown = await alarmTool.execute({ action: "alarm_disarm", entity_id: "alarm_control_panel.home", code: "1234", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should list alarms", async () => { const mockAlarms = [ { entity_id: "alarm_control_panel.home", state: "armed_away", attributes: {}, }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockAlarms)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const alarmTool = tools.find((t) => t.name === "alarm_control"); expect(alarmTool).toBeDefined(); if (!alarmTool) throw new Error("alarm_control tool not found"); const result: unknown = await alarmTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Automation Tool", () => { test("should list automations", async () => { const mockAutomations = [ { entity_id: "automation.test", state: "on", attributes: { friendly_name: "Test Automation" }, }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockAutomations)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const automationTool = tools.find((t) => t.name === "automation"); expect(automationTool).toBeDefined(); if (!automationTool) throw new Error("automation tool not found"); const result: unknown = await automationTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should toggle automation", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const automationTool = tools.find((t) => t.name === "automation"); expect(automationTool).toBeDefined(); if (!automationTool) throw new Error("automation tool not found"); const result: unknown = await automationTool.execute({ action: "toggle", automation_id: "automation.test", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Scene Control Tool", () => { test("should list scenes", async () => { const mockScenes = [ { entity_id: "scene.movie", state: "scanned", attributes: { friendly_name: "Movie Scene" }, }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockScenes)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const sceneTool = tools.find((t) => t.name === "scene"); expect(sceneTool).toBeDefined(); if (!sceneTool) throw new Error("scene tool not found"); const result: unknown = await sceneTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should activate scene", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const sceneTool = tools.find((t) => t.name === "scene"); expect(sceneTool).toBeDefined(); if (!sceneTool) throw new Error("scene tool not found"); const result: unknown = await sceneTool.execute({ action: "activate", scene_id: "scene.movie", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Notify Tool", () => { test("should send notification", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const notifyTool = tools.find((t) => t.name === "notify"); expect(notifyTool).toBeDefined(); if (!notifyTool) throw new Error("notify tool not found"); const result: unknown = await notifyTool.execute({ message: "Test notification", title: "Test", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should send notification to specific target", async () => { mocks.mockFetch = mock(() => Promise.resolve(createMockResponse({}))); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const notifyTool = tools.find((t) => t.name === "notify"); expect(notifyTool).toBeDefined(); if (!notifyTool) throw new Error("notify tool not found"); const result: unknown = await notifyTool.execute({ message: "Test notification", target: "mobile_app_phone", }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("List Devices Tool", () => { test("should list all devices", async () => { const mockDevices = [ { entity_id: "light.living_room", state: "on" }, { entity_id: "climate.bedroom", state: "heat" }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockDevices)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const listDevicesTool = tools.find( (t) => t.name === "list_devices" ); expect(listDevicesTool).toBeDefined(); if (!listDevicesTool) throw new Error("list_devices tool not found"); const result: unknown = await listDevicesTool.execute({}); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); test("should filter devices by domain", async () => { const mockDevices = [ { entity_id: "light.living_room", state: "on" }, ]; mocks.mockFetch = mock(() => Promise.resolve(createMockResponse(mockDevices)) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const listDevicesTool = tools.find( (t) => t.name === "list_devices" ); expect(listDevicesTool).toBeDefined(); if (!listDevicesTool) throw new Error("list_devices tool not found"); const result: unknown = await listDevicesTool.execute({ domain: "light" }); const parsed = parseResult(result); expect(parsed.success).toBe(true); }); }); describe("Error Handling Across All Tools", () => { test("should handle network errors gracefully", async () => { mocks.mockFetch = mock(() => Promise.reject(new Error("Network timeout")) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; // Test a few tools for error handling const controlTool = tools.find((t) => t.name === "control"); if (!controlTool) throw new Error("control tool not found"); const result: unknown = await controlTool.execute({ command: "turn_on", entity_id: "light.test", }); const parsed = parseResult(result); expect(parsed.success).toBe(false); }); test("should handle 4xx HTTP errors", async () => { mocks.mockFetch = mock(() => Promise.resolve( new Response(null, { status: 404, statusText: "Not Found" }) ) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const historyTool = tools.find((t) => t.name === "get_history"); if (!historyTool) throw new Error("get_history tool not found"); const result: unknown = await historyTool.execute({ entity_id: "light.nonexistent", }); const parsed = parseResult(result); expect(parsed.success).toBe(false); }); test("should handle 5xx HTTP errors", async () => { mocks.mockFetch = mock(() => Promise.resolve( new Response(null, { status: 500, statusText: "Internal Server Error", }) ) ); globalThis.fetch = mocks.mockFetch as unknown as typeof fetch; const addonTool = tools.find((t) => t.name === "addon"); if (!addonTool) throw new Error("addon tool not found"); const result: unknown = await addonTool.execute({ action: "list" }); const parsed = parseResult(result); expect(parsed.success).toBe(false); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jango-blockchained/advanced-homeassistant-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server