#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ErrorCode,
McpError
} from '@modelcontextprotocol/sdk/types.js';
import { EcoFlowClient } from './client.js';
// Tool imports
import { listDevicesTool, executeListDevices } from './tools/list-devices.js';
import { getDeviceStatusTool, executeGetDeviceStatus } from './tools/get-device-status.js';
import { getSolarInputTool, executeGetSolarInput } from './tools/get-solar-input.js';
import { setAcOutputTool, executeSetAcOutput } from './tools/set-ac-output.js';
import { setDcOutputTool, executeSetDcOutput } from './tools/set-dc-output.js';
import { getChargingSettingsTool, executeGetChargingSettings } from './tools/get-charging-settings.js';
import { setChargingSettingsTool, executeSetChargingSettings } from './tools/set-charging-settings.js';
import { getRawQuotaTool, executeGetRawQuota } from './tools/get-raw-quota.js';
import { setStandbyTool, executeSetStandby } from './tools/set-standby.js';
// Get credentials from environment
const ACCESS_KEY = process.env.ECOFLOW_ACCESS_KEY;
const SECRET_KEY = process.env.ECOFLOW_SECRET_KEY;
const BASE_URL = process.env.ECOFLOW_BASE_URL;
if (!ACCESS_KEY || !SECRET_KEY) {
console.error('Error: ECOFLOW_ACCESS_KEY and ECOFLOW_SECRET_KEY environment variables are required');
console.error('');
console.error('To get your credentials:');
console.error('1. Register at https://developer.ecoflow.com');
console.error('2. Wait for approval (may take up to a week)');
console.error('3. Generate Access Key and Secret Key from your profile');
console.error('');
console.error('Set them in your MCP configuration:');
console.error(' ECOFLOW_ACCESS_KEY=your_access_key');
console.error(' ECOFLOW_SECRET_KEY=your_secret_key');
console.error(' ECOFLOW_BASE_URL=https://api.ecoflow.com (optional, for EU use https://api-e.ecoflow.com)');
process.exit(1);
}
// Create EcoFlow client
const client = new EcoFlowClient({
accessKey: ACCESS_KEY,
secretKey: SECRET_KEY,
baseUrl: BASE_URL
});
// Create MCP server
const server = new Server(
{
name: 'ecoflow-mcp',
version: '1.0.0'
},
{
capabilities: {
tools: {}
}
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
listDevicesTool,
getDeviceStatusTool,
getSolarInputTool,
setAcOutputTool,
setDcOutputTool,
getChargingSettingsTool,
setChargingSettingsTool,
getRawQuotaTool,
setStandbyTool
]
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
let result: string;
switch (name) {
case 'ecoflow_list_devices':
result = await executeListDevices(client);
break;
case 'ecoflow_get_device_status':
result = await executeGetDeviceStatus(client, args as { serial_number: string });
break;
case 'ecoflow_get_solar_input':
result = await executeGetSolarInput(client, args as { serial_number: string });
break;
case 'ecoflow_set_ac_output':
result = await executeSetAcOutput(client, args as {
serial_number: string;
enabled: boolean;
xboost?: boolean;
voltage?: number;
frequency?: number;
});
break;
case 'ecoflow_set_dc_output':
result = await executeSetDcOutput(client, args as {
serial_number: string;
enabled: boolean;
});
break;
case 'ecoflow_get_charging_settings':
result = await executeGetChargingSettings(client, args as { serial_number: string });
break;
case 'ecoflow_set_charging_settings':
result = await executeSetChargingSettings(client, args as {
serial_number: string;
max_charge_soc?: number;
min_discharge_soc?: number;
charging_watts?: number;
});
break;
case 'ecoflow_get_raw_quota':
result = await executeGetRawQuota(client, args as {
serial_number: string;
filter?: string;
});
break;
case 'ecoflow_set_standby':
result = await executeSetStandby(client, args as {
serial_number: string;
device_timeout?: number;
ac_timeout?: number;
dc_timeout?: number;
lcd_timeout?: number;
});
break;
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
return {
content: [
{
type: 'text',
text: result
}
]
};
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (error instanceof McpError) {
throw error;
}
return {
content: [
{
type: 'text',
text: `Error: ${message}`
}
],
isError: true
};
}
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('EcoFlow MCP server running on stdio');
}
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});