Home Assistant MCP Tools - Fixes Summary¶
Problems Identified¶
1. Duplicate Tool Registration ❌¶
Issue: Tools were being registered twice in different places:
- Once from src/tools/*.tool.ts (plain Tool objects using fetch API)
- Once from src/tools/homeassistant/*.tool.ts (BaseTool classes using get_hass)
Affected Tools: - automation - list_devices - notify - scene
2. Circular Dependency ❌¶
Issue: Cannot access 'tools' before initialization error in src/index.ts
- Line 27: imports tools array from ./tools/index.js
- Line 61-63: tries to register tools from the array
- The tools array included homeassistant tools that were already registered as classes
3. Inconsistent Tool Exports ❌¶
Issue: Some homeassistant tools only exported BaseTool classes, not Tool objects - lights and climate: exported both Tool objects and classes ✓ - automation, list_devices, notify, scene: only exported classes ✗
This caused issues in stdio-server.ts which needed Tool objects for FastMCP.
Solutions Applied¶
1. Removed Duplicate Tool Files ✅¶
Deleted duplicate implementations from src/tools/:
rm src/tools/automation.tool.ts
rm src/tools/list-devices.tool.ts
rm src/tools/notify.tool.ts
rm src/tools/scene.tool.ts
2. Enhanced Homeassistant Tools ✅¶
Added Tool object exports to all homeassistant tools following the pattern:
// Shared execution logic
async function executeToolLogic(params: ToolParams): Promise<Record<string, unknown>> {
// Implementation
}
// Tool object export (for FastMCP/stdio)
export const toolObject: Tool = {
name: "tool_name",
description: "Description",
parameters: toolSchema,
execute: executeToolLogic
};
// BaseTool class export (for HTTP/MCPServer)
export class ToolClass extends BaseTool {
constructor() {
super({
name: toolObject.name,
description: toolObject.description,
parameters: toolSchema,
// ...
});
}
public async execute(params: ToolParams, context: MCPContext): Promise<Record<string, unknown>> {
const validatedParams = this.validateParams(params);
return await executeToolLogic(validatedParams);
}
}
3. Updated Tools Registry ✅¶
Modified src/tools/index.ts to include all homeassistant Tool objects:
import { lightsControlTool } from "./homeassistant/lights.tool.js";
import { climateControlTool } from "./homeassistant/climate.tool.js";
import { automationTool } from "./homeassistant/automation.tool.js";
import { listDevicesTool } from "./homeassistant/list-devices.tool.js";
import { notifyTool } from "./homeassistant/notify.tool.js";
import { sceneTool } from "./homeassistant/scene.tool.js";
import { mediaPlayerControlTool } from "./homeassistant/media-player.tool.js";
import { coverControlTool } from "./homeassistant/cover.tool.js";
import { lockControlTool } from "./homeassistant/lock.tool.js";
import { fanControlTool } from "./homeassistant/fan.tool.js";
import { vacuumControlTool } from "./homeassistant/vacuum.tool.js";
import { alarmControlTool } from "./homeassistant/alarm.tool.js";
export const tools: Tool[] = [
// ... other tools
lightsControlTool,
climateControlTool,
automationTool,
listDevicesTool,
notifyTool,
sceneTool,
mediaPlayerControlTool,
coverControlTool,
lockControlTool,
fanControlTool,
vacuumControlTool,
alarmControlTool,
];
4. Fixed Tool Registration in src/index.ts ✅¶
Filtered homeassistant tools from the tools array to avoid duplicate registration:
// Register Home Assistant tools (BaseTool classes)
server.registerTool(new LightsControlTool());
server.registerTool(new ClimateControlTool());
server.registerTool(new ListDevicesTool());
server.registerTool(new AutomationTool());
server.registerTool(new SceneTool());
server.registerTool(new NotifyTool());
server.registerTool(new MediaPlayerControlTool());
server.registerTool(new CoverControlTool());
server.registerTool(new LockControlTool());
server.registerTool(new FanControlTool());
server.registerTool(new VacuumControlTool());
server.registerTool(new AlarmControlTool());
// Register additional tools (excluding homeassistant tools already registered)
const homeAssistantToolNames = [
'lights_control', 'climate_control', 'list_devices', 'automation', 'scene', 'notify',
'media_player_control', 'cover_control', 'lock_control', 'fan_control', 'vacuum_control', 'alarm_control'
];
tools.forEach(tool => {
if (!homeAssistantToolNames.includes(tool.name)) {
server.registerTool(tool);
}
});
5. Simplified stdio-server.ts ✅¶
Removed duplicate registration since all tools are now in the tools array:
// Add all tools from the tools registry
for (const tool of tools) {
server.addTool({
name: tool.name,
description: tool.description,
parameters: tool.parameters as never,
execute: tool.execute
});
logger.info(`Added tool: ${tool.name}`);
}
6. Fixed TypeScript Errors ✅¶
- Fixed climate.tool.ts: Made temperature parameter optional
- Updated test expectations to match new return formats
Results¶
✅ All Tests Passing¶
10 tests pass
0 tests fail
✅ Build Succeeds¶
bun build ./src/index.ts --outdir ./dist --target bun --minify
Bundled 763 modules in 76ms
index.js 1.73 MB (entry point)
✅ Circular Dependency Resolved¶
No more "Cannot access 'tools' before initialization" errors
✅ No Duplicate Registrations¶
Each tool is registered exactly once in the appropriate transport
Architecture¶
Tool Types¶
- Tool Object (for FastMCP/stdio transport):
- Plain JavaScript object
- Has
name,description,parameters, andexecuteproperties -
Used by stdio-server.ts
-
BaseTool Class (for HTTP/MCPServer transport):
- Extends the BaseTool class
- Has additional methods like
validateParams,validateResult - Used by src/index.ts
Tool Organization¶
src/tools/
├── homeassistant/ # Core HA tools (export both formats)
│ ├── lights.tool.ts # ✓ lightsControlTool + LightsControlTool
│ ├── climate.tool.ts # ✓ climateControlTool + ClimateControlTool
│ ├── automation.tool.ts # ✓ automationTool + AutomationTool
│ ├── list-devices.tool.ts # ✓ listDevicesTool + ListDevicesTool
│ ├── notify.tool.ts # ✓ notifyTool + NotifyTool
│ ├── scene.tool.ts # ✓ sceneTool + SceneTool
│ ├── media-player.tool.ts # ✓ mediaPlayerControlTool + MediaPlayerControlTool
│ ├── cover.tool.ts # ✓ coverControlTool + CoverControlTool
│ ├── lock.tool.ts # ✓ lockControlTool + LockControlTool
│ ├── fan.tool.ts # ✓ fanControlTool + FanControlTool
│ ├── vacuum.tool.ts # ✓ vacuumControlTool + VacuumControlTool
│ └── alarm.tool.ts # ✓ alarmControlTool + AlarmControlTool
├── control.tool.ts # General device control
├── history.tool.ts # History queries
├── addon.tool.ts # Add-on management
├── package.tool.ts # Package management
├── automation-config.tool.ts # Automation configuration
├── subscribe-events.tool.ts # Event subscription
├── sse-stats.tool.ts # SSE statistics
└── index.ts # Tools registry (exports all Tool objects)
Key Takeaways¶
-
Dual Export Pattern: Homeassistant tools now export both Tool objects and BaseTool classes to support both transport types
-
Single Source of Truth: Each tool's logic is in one place (the shared execution function)
-
Clear Separation:
- src/index.ts uses BaseTool classes (for MCPServer)
- stdio-server.ts uses Tool objects (for FastMCP)
-
tools/index.ts exports all Tool objects
-
No Duplicates: Filtering prevents double registration in src/index.ts
Remaining Items (Non-Critical)¶
- Some ESLint warnings about strict type checking (not affecting functionality)
- Pre-existing linting errors in other files (unrelated to this fix)