@switchbot/openapi-cli 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -18
- package/dist/api/client.d.ts +7 -1
- package/dist/api/client.js +44 -8
- package/dist/api/client.js.map +1 -1
- package/dist/commands/batch.d.ts +2 -0
- package/dist/commands/batch.js +252 -0
- package/dist/commands/batch.js.map +1 -0
- package/dist/commands/cache.d.ts +2 -0
- package/dist/commands/cache.js +108 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/capabilities.d.ts +2 -0
- package/dist/commands/capabilities.js +91 -0
- package/dist/commands/capabilities.js.map +1 -0
- package/dist/commands/catalog.d.ts +2 -0
- package/dist/commands/catalog.js +291 -0
- package/dist/commands/catalog.js.map +1 -0
- package/dist/commands/config.js +123 -10
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/device-meta.d.ts +2 -0
- package/dist/commands/device-meta.js +142 -0
- package/dist/commands/device-meta.js.map +1 -0
- package/dist/commands/devices.js +272 -152
- package/dist/commands/devices.js.map +1 -1
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +147 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/events.d.ts +15 -0
- package/dist/commands/events.js +188 -0
- package/dist/commands/events.js.map +1 -0
- package/dist/commands/expand.d.ts +2 -0
- package/dist/commands/expand.js +192 -0
- package/dist/commands/expand.js.map +1 -0
- package/dist/commands/explain.d.ts +2 -0
- package/dist/commands/explain.js +137 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/history.d.ts +2 -0
- package/dist/commands/history.js +104 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/mcp.d.ts +4 -0
- package/dist/commands/mcp.js +386 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/plan.d.ts +38 -0
- package/dist/commands/plan.js +356 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/quota.d.ts +2 -0
- package/dist/commands/quota.js +77 -0
- package/dist/commands/quota.js.map +1 -0
- package/dist/commands/scenes.js +19 -13
- package/dist/commands/scenes.js.map +1 -1
- package/dist/commands/schema.d.ts +2 -0
- package/dist/commands/schema.js +77 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/watch.d.ts +2 -0
- package/dist/commands/watch.js +161 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/commands/webhook.js +37 -22
- package/dist/commands/webhook.js.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.js +32 -6
- package/dist/config.js.map +1 -1
- package/dist/devices/cache.d.ts +50 -0
- package/dist/devices/cache.js +152 -1
- package/dist/devices/cache.js.map +1 -1
- package/dist/devices/catalog.d.ts +49 -0
- package/dist/devices/catalog.js +362 -92
- package/dist/devices/catalog.js.map +1 -1
- package/dist/devices/device-meta.d.ts +15 -0
- package/dist/devices/device-meta.js +52 -0
- package/dist/devices/device-meta.js.map +1 -0
- package/dist/index.js +31 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/devices.d.ts +144 -0
- package/dist/lib/devices.js +329 -0
- package/dist/lib/devices.js.map +1 -0
- package/dist/lib/scenes.d.ts +7 -0
- package/dist/lib/scenes.js +11 -0
- package/dist/lib/scenes.js.map +1 -0
- package/dist/utils/audit.d.ts +13 -0
- package/dist/utils/audit.js +43 -0
- package/dist/utils/audit.js.map +1 -0
- package/dist/utils/filter.d.ts +45 -0
- package/dist/utils/filter.js +96 -0
- package/dist/utils/filter.js.map +1 -0
- package/dist/utils/flags.d.ts +42 -0
- package/dist/utils/flags.js +108 -0
- package/dist/utils/flags.js.map +1 -1
- package/dist/utils/format.d.ts +9 -0
- package/dist/utils/format.js +109 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/name-resolver.d.ts +17 -0
- package/dist/utils/name-resolver.js +78 -0
- package/dist/utils/name-resolver.js.map +1 -0
- package/dist/utils/output.d.ts +18 -0
- package/dist/utils/output.js +66 -6
- package/dist/utils/output.js.map +1 -1
- package/dist/utils/quota.d.ts +48 -0
- package/dist/utils/quota.js +144 -0
- package/dist/utils/quota.js.map +1 -0
- package/dist/utils/retry.d.ts +23 -0
- package/dist/utils/retry.js +60 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/string.d.ts +2 -0
- package/dist/utils/string.js +23 -0
- package/dist/utils/string.js.map +1 -0
- package/package.json +4 -1
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { handleError, isJsonMode } from '../utils/output.js';
|
|
6
|
+
import { fetchDeviceList, fetchDeviceStatus, executeCommand, describeDevice, validateCommand, isDestructiveCommand, getDestructiveReason, searchCatalog, DeviceNotFoundError, toMcpDescribeShape, toMcpDeviceListShape, toMcpIrDeviceShape, } from '../lib/devices.js';
|
|
7
|
+
import { fetchScenes, executeScene } from '../lib/scenes.js';
|
|
8
|
+
import { findCatalogEntry } from '../devices/catalog.js';
|
|
9
|
+
import { getCachedDevice } from '../devices/cache.js';
|
|
10
|
+
function mcpError(kind, code, message, options) {
|
|
11
|
+
const obj = { code, kind, message };
|
|
12
|
+
if (options?.hint)
|
|
13
|
+
obj.hint = options.hint;
|
|
14
|
+
if (options?.retryable)
|
|
15
|
+
obj.retryable = true;
|
|
16
|
+
if (options?.context)
|
|
17
|
+
obj.context = options.context;
|
|
18
|
+
return {
|
|
19
|
+
isError: true,
|
|
20
|
+
content: [{ type: 'text', text: JSON.stringify({ error: obj }, null, 2) }],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function createSwitchBotMcpServer() {
|
|
24
|
+
const server = new McpServer({
|
|
25
|
+
name: 'switchbot',
|
|
26
|
+
version: '1.4.0',
|
|
27
|
+
}, {
|
|
28
|
+
capabilities: { tools: {} },
|
|
29
|
+
instructions: `SwitchBot is an IoT smart home brand by Wonderlabs, Inc. This MCP server controls physical devices \
|
|
30
|
+
(Bot, Curtain, Smart Lock, Color Bulb, Meter, Plug, Robot Vacuum, etc.) and IR remotes \
|
|
31
|
+
(TV, AC, Set Top Box, etc.) via the SwitchBot Cloud API v1.1.
|
|
32
|
+
|
|
33
|
+
Device categories:
|
|
34
|
+
- physical: Wi-Fi/BLE devices; BLE-only ones require a Hub (check enableCloudService)
|
|
35
|
+
- ir: IR remotes learned by a Hub; no status channel, commands only
|
|
36
|
+
|
|
37
|
+
Key constraints:
|
|
38
|
+
- API quota: 10,000 requests/day per account — use cache, avoid polling
|
|
39
|
+
- Destructive commands (unlock, garage open, keypad createKey/deleteKey) require confirm:true
|
|
40
|
+
- Devices without enableCloudService cannot receive commands via API
|
|
41
|
+
|
|
42
|
+
Recommended bootstrap sequence:
|
|
43
|
+
1. list_devices → get deviceIds and categories
|
|
44
|
+
2. search_catalog or describe_device → confirm supported commands offline/online
|
|
45
|
+
3. send_command (with confirm:true for destructive commands)
|
|
46
|
+
|
|
47
|
+
API docs: https://github.com/OpenWonderLabs/SwitchBotAPI`,
|
|
48
|
+
});
|
|
49
|
+
// ---- list_devices ---------------------------------------------------------
|
|
50
|
+
server.registerTool('list_devices', {
|
|
51
|
+
title: 'List all devices on the account',
|
|
52
|
+
description: 'Fetch the inventory of physical devices and IR remotes on this SwitchBot account. Refreshes the local cache.',
|
|
53
|
+
inputSchema: {},
|
|
54
|
+
outputSchema: {
|
|
55
|
+
deviceList: z.array(z.object({
|
|
56
|
+
deviceId: z.string(),
|
|
57
|
+
deviceName: z.string(),
|
|
58
|
+
deviceType: z.string().optional(),
|
|
59
|
+
enableCloudService: z.boolean(),
|
|
60
|
+
hubDeviceId: z.string(),
|
|
61
|
+
roomID: z.string().optional(),
|
|
62
|
+
roomName: z.string().nullable().optional(),
|
|
63
|
+
familyName: z.string().optional(),
|
|
64
|
+
controlType: z.string().optional(),
|
|
65
|
+
}).passthrough()).describe('Physical SwitchBot devices'),
|
|
66
|
+
infraredRemoteList: z.array(z.object({
|
|
67
|
+
deviceId: z.string(),
|
|
68
|
+
deviceName: z.string(),
|
|
69
|
+
remoteType: z.string(),
|
|
70
|
+
hubDeviceId: z.string(),
|
|
71
|
+
controlType: z.string().optional(),
|
|
72
|
+
}).passthrough()).describe('IR remote devices'),
|
|
73
|
+
},
|
|
74
|
+
}, async () => {
|
|
75
|
+
const body = await fetchDeviceList();
|
|
76
|
+
return {
|
|
77
|
+
content: [{ type: 'text', text: JSON.stringify(body, null, 2) }],
|
|
78
|
+
structuredContent: {
|
|
79
|
+
deviceList: body.deviceList.map(toMcpDeviceListShape),
|
|
80
|
+
infraredRemoteList: body.infraredRemoteList.map(toMcpIrDeviceShape),
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
// ---- get_device_status ----------------------------------------------------
|
|
85
|
+
server.registerTool('get_device_status', {
|
|
86
|
+
title: 'Get live status for a device',
|
|
87
|
+
description: 'Query the real-time status payload for a physical device. IR remotes have no status channel and will error.',
|
|
88
|
+
inputSchema: {
|
|
89
|
+
deviceId: z.string().describe('Device ID from list_devices'),
|
|
90
|
+
},
|
|
91
|
+
outputSchema: {
|
|
92
|
+
status: z.object({
|
|
93
|
+
deviceId: z.string().optional(),
|
|
94
|
+
deviceType: z.string().optional(),
|
|
95
|
+
hubDeviceId: z.string().optional(),
|
|
96
|
+
connectionStatus: z.string().optional(),
|
|
97
|
+
}).passthrough().describe('Live device status (deviceId + deviceType + device-specific fields)'),
|
|
98
|
+
},
|
|
99
|
+
}, async ({ deviceId }) => {
|
|
100
|
+
const body = await fetchDeviceStatus(deviceId);
|
|
101
|
+
return {
|
|
102
|
+
content: [{ type: 'text', text: JSON.stringify(body, null, 2) }],
|
|
103
|
+
structuredContent: { status: body },
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
// ---- send_command ---------------------------------------------------------
|
|
107
|
+
server.registerTool('send_command', {
|
|
108
|
+
title: 'Send a control command to a device',
|
|
109
|
+
description: 'Send a control command (turnOn, setColor, startClean, unlock, ...) to a device. Destructive commands (unlock, garage open, keypad createKey) require confirm:true; otherwise they are rejected.',
|
|
110
|
+
inputSchema: {
|
|
111
|
+
deviceId: z.string().describe('Device ID from list_devices'),
|
|
112
|
+
command: z.string().describe('Command name, case-sensitive (e.g. turnOn, setColor, unlock)'),
|
|
113
|
+
parameter: z
|
|
114
|
+
.union([z.string(), z.number(), z.boolean(), z.record(z.string(), z.unknown()), z.array(z.unknown())])
|
|
115
|
+
.optional()
|
|
116
|
+
.describe('Command parameter. Omit for no-arg commands.'),
|
|
117
|
+
commandType: z
|
|
118
|
+
.enum(['command', 'customize'])
|
|
119
|
+
.optional()
|
|
120
|
+
.default('command')
|
|
121
|
+
.describe('"command" for built-in commands; "customize" for user-defined IR buttons'),
|
|
122
|
+
confirm: z
|
|
123
|
+
.boolean()
|
|
124
|
+
.optional()
|
|
125
|
+
.default(false)
|
|
126
|
+
.describe('Required true for destructive commands (unlock, garage open, createKey, ...)'),
|
|
127
|
+
},
|
|
128
|
+
outputSchema: {
|
|
129
|
+
ok: z.literal(true),
|
|
130
|
+
command: z.string(),
|
|
131
|
+
deviceId: z.string(),
|
|
132
|
+
result: z.unknown().describe('API response body from SwitchBot'),
|
|
133
|
+
},
|
|
134
|
+
}, async ({ deviceId, command, parameter, commandType, confirm }) => {
|
|
135
|
+
const effectiveType = commandType ?? 'command';
|
|
136
|
+
// Resolve the device's catalog type via cache or a fresh lookup so we
|
|
137
|
+
// can evaluate destructive/validation without an extra round-trip if
|
|
138
|
+
// the cache is warm.
|
|
139
|
+
let typeName = getCachedDevice(deviceId)?.type;
|
|
140
|
+
if (!typeName) {
|
|
141
|
+
const body = await fetchDeviceList();
|
|
142
|
+
const physical = body.deviceList.find((d) => d.deviceId === deviceId);
|
|
143
|
+
const ir = body.infraredRemoteList.find((d) => d.deviceId === deviceId);
|
|
144
|
+
if (!physical && !ir) {
|
|
145
|
+
return mcpError('runtime', 152, `Device not found: ${deviceId}`, {
|
|
146
|
+
hint: "Check the deviceId with 'switchbot devices list' (IDs are case-sensitive).",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
typeName = physical ? physical.deviceType : ir.remoteType;
|
|
150
|
+
}
|
|
151
|
+
if (isDestructiveCommand(typeName, command, effectiveType) && !confirm) {
|
|
152
|
+
const reason = getDestructiveReason(typeName, command, effectiveType);
|
|
153
|
+
const entry = typeName ? findCatalogEntry(typeName) : null;
|
|
154
|
+
const spec = entry && !Array.isArray(entry)
|
|
155
|
+
? entry.commands.find((c) => c.command === command)
|
|
156
|
+
: undefined;
|
|
157
|
+
const hint = reason
|
|
158
|
+
? `Re-issue with confirm:true after confirming with the user. Reason: ${reason}`
|
|
159
|
+
: 'Re-issue the call with confirm:true to proceed.';
|
|
160
|
+
return mcpError('guard', 3, `Command "${command}" on device type "${typeName}" is destructive and requires confirm:true.`, {
|
|
161
|
+
hint,
|
|
162
|
+
context: {
|
|
163
|
+
command,
|
|
164
|
+
deviceType: typeName,
|
|
165
|
+
description: spec?.description ?? null,
|
|
166
|
+
...(reason ? { destructiveReason: reason } : {}),
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// stringifiedParam is what validateCommand expects to decide
|
|
171
|
+
// "no-parameter" conflicts — mirror the CLI behavior.
|
|
172
|
+
const stringifiedParam = parameter === undefined ? undefined : typeof parameter === 'string' ? parameter : JSON.stringify(parameter);
|
|
173
|
+
const validation = validateCommand(deviceId, command, stringifiedParam, effectiveType);
|
|
174
|
+
if (!validation.ok) {
|
|
175
|
+
return mcpError('usage', 2, validation.error.message, { hint: validation.error.hint, context: { validationKind: validation.error.kind } });
|
|
176
|
+
}
|
|
177
|
+
const result = await executeCommand(deviceId, command, parameter, effectiveType);
|
|
178
|
+
const structured = { ok: true, command, deviceId, result };
|
|
179
|
+
return {
|
|
180
|
+
content: [{ type: 'text', text: JSON.stringify(structured, null, 2) }],
|
|
181
|
+
structuredContent: structured,
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
// ---- run_scene ------------------------------------------------------------
|
|
185
|
+
server.registerTool('run_scene', {
|
|
186
|
+
title: 'Execute a manual scene',
|
|
187
|
+
description: 'Execute a manual SwitchBot scene by its sceneId (from list_scenes).',
|
|
188
|
+
inputSchema: {
|
|
189
|
+
sceneId: z.string().describe('Scene ID from list_scenes'),
|
|
190
|
+
},
|
|
191
|
+
outputSchema: {
|
|
192
|
+
ok: z.literal(true),
|
|
193
|
+
sceneId: z.string(),
|
|
194
|
+
},
|
|
195
|
+
}, async ({ sceneId }) => {
|
|
196
|
+
await executeScene(sceneId);
|
|
197
|
+
const structured = { ok: true, sceneId };
|
|
198
|
+
return {
|
|
199
|
+
content: [{ type: 'text', text: JSON.stringify(structured, null, 2) }],
|
|
200
|
+
structuredContent: structured,
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
// ---- list_scenes (companion to run_scene) ---------------------------------
|
|
204
|
+
server.registerTool('list_scenes', {
|
|
205
|
+
title: 'List all manual scenes',
|
|
206
|
+
description: 'Fetch all manual scenes configured in the SwitchBot app.',
|
|
207
|
+
inputSchema: {},
|
|
208
|
+
outputSchema: {
|
|
209
|
+
scenes: z.array(z.object({ sceneId: z.string(), sceneName: z.string() })),
|
|
210
|
+
},
|
|
211
|
+
}, async () => {
|
|
212
|
+
const scenes = await fetchScenes();
|
|
213
|
+
return {
|
|
214
|
+
content: [{ type: 'text', text: JSON.stringify(scenes, null, 2) }],
|
|
215
|
+
structuredContent: { scenes },
|
|
216
|
+
};
|
|
217
|
+
});
|
|
218
|
+
// ---- search_catalog -------------------------------------------------------
|
|
219
|
+
server.registerTool('search_catalog', {
|
|
220
|
+
title: 'Search the offline device catalog',
|
|
221
|
+
description: 'Search the built-in device catalog by type name or alias. Returns matching entries with their commands, roles, destructive flags, and status fields. No API call.',
|
|
222
|
+
inputSchema: {
|
|
223
|
+
query: z.string().describe('Search query (matches type and aliases, case-insensitive). Use empty string to list all.'),
|
|
224
|
+
limit: z.number().int().min(1).max(100).optional().default(20).describe('Max entries returned (default 20)'),
|
|
225
|
+
},
|
|
226
|
+
outputSchema: {
|
|
227
|
+
results: z.array(z.object({
|
|
228
|
+
type: z.string(),
|
|
229
|
+
category: z.enum(['physical', 'ir']),
|
|
230
|
+
commands: z.array(z.object({
|
|
231
|
+
command: z.string(),
|
|
232
|
+
parameter: z.string(),
|
|
233
|
+
description: z.string(),
|
|
234
|
+
commandType: z.enum(['command', 'customize']).optional(),
|
|
235
|
+
idempotent: z.boolean().optional(),
|
|
236
|
+
destructive: z.boolean().optional(),
|
|
237
|
+
}).passthrough()),
|
|
238
|
+
aliases: z.array(z.string()).optional(),
|
|
239
|
+
statusFields: z.array(z.string()).optional(),
|
|
240
|
+
role: z.string().optional(),
|
|
241
|
+
readOnly: z.boolean().optional(),
|
|
242
|
+
}).passthrough()).describe('Matching catalog entries'),
|
|
243
|
+
total: z.number().int().describe('Number of entries returned'),
|
|
244
|
+
},
|
|
245
|
+
}, async ({ query, limit }) => {
|
|
246
|
+
const hits = searchCatalog(query, limit);
|
|
247
|
+
const structured = { results: hits, total: hits.length };
|
|
248
|
+
return {
|
|
249
|
+
content: [{ type: 'text', text: JSON.stringify(hits, null, 2) }],
|
|
250
|
+
structuredContent: structured,
|
|
251
|
+
};
|
|
252
|
+
});
|
|
253
|
+
// ---- describe_device ------------------------------------------------------
|
|
254
|
+
server.registerTool('describe_device', {
|
|
255
|
+
title: 'Describe a specific device',
|
|
256
|
+
description: 'Resolve a deviceId to its metadata + catalog entry + suggested safe actions. Pass live:true to also fetch real-time status values.',
|
|
257
|
+
inputSchema: {
|
|
258
|
+
deviceId: z.string().describe('Device ID from list_devices'),
|
|
259
|
+
live: z.boolean().optional().default(false).describe('Also fetch live /status values (costs 1 extra API call)'),
|
|
260
|
+
},
|
|
261
|
+
outputSchema: {
|
|
262
|
+
device: z.object({
|
|
263
|
+
device: z.object({ deviceId: z.string(), deviceName: z.string() }).passthrough(),
|
|
264
|
+
isPhysical: z.boolean(),
|
|
265
|
+
typeName: z.string(),
|
|
266
|
+
controlType: z.string().nullable(),
|
|
267
|
+
source: z.enum(['catalog', 'live', 'catalog+live', 'none']),
|
|
268
|
+
capabilities: z.unknown().nullable(),
|
|
269
|
+
suggestedActions: z.array(z.object({
|
|
270
|
+
command: z.string(),
|
|
271
|
+
parameter: z.string().optional(),
|
|
272
|
+
description: z.string(),
|
|
273
|
+
})).optional(),
|
|
274
|
+
inheritedLocation: z.object({
|
|
275
|
+
family: z.string().optional(),
|
|
276
|
+
room: z.string().optional(),
|
|
277
|
+
}).optional(),
|
|
278
|
+
}).passthrough().describe('Device metadata, catalog entry, capabilities, and optional live status'),
|
|
279
|
+
},
|
|
280
|
+
}, async ({ deviceId, live }) => {
|
|
281
|
+
try {
|
|
282
|
+
const result = await describeDevice(deviceId, { live });
|
|
283
|
+
return {
|
|
284
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
285
|
+
structuredContent: { device: toMcpDescribeShape(result) },
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
if (err instanceof DeviceNotFoundError) {
|
|
290
|
+
return mcpError('runtime', 152, err.message, {
|
|
291
|
+
hint: "Check the deviceId with 'switchbot devices list' (IDs are case-sensitive).",
|
|
292
|
+
context: { deviceId },
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
throw err;
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
return server;
|
|
299
|
+
}
|
|
300
|
+
export function registerMcpCommand(program) {
|
|
301
|
+
const mcp = program
|
|
302
|
+
.command('mcp')
|
|
303
|
+
.description('Run as a Model Context Protocol server so AI agents can call SwitchBot tools')
|
|
304
|
+
.addHelpText('after', `
|
|
305
|
+
The MCP server exposes seven tools over stdio:
|
|
306
|
+
- list_devices fetch all physical + IR devices
|
|
307
|
+
- get_device_status live status for a physical device
|
|
308
|
+
- send_command control a device (destructive commands need confirm:true)
|
|
309
|
+
- list_scenes list all manual scenes
|
|
310
|
+
- run_scene execute a manual scene
|
|
311
|
+
- search_catalog offline catalog search by type/alias
|
|
312
|
+
- describe_device metadata + commands + (optionally) live status for one device
|
|
313
|
+
|
|
314
|
+
Example Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
|
|
315
|
+
|
|
316
|
+
{
|
|
317
|
+
"mcpServers": {
|
|
318
|
+
"switchbot": {
|
|
319
|
+
"command": "switchbot",
|
|
320
|
+
"args": ["mcp", "serve"],
|
|
321
|
+
"env": {
|
|
322
|
+
"SWITCHBOT_TOKEN": "...",
|
|
323
|
+
"SWITCHBOT_SECRET": "..."
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
Inspect locally:
|
|
330
|
+
$ npx @modelcontextprotocol/inspector switchbot mcp serve
|
|
331
|
+
`);
|
|
332
|
+
mcp
|
|
333
|
+
.command('serve')
|
|
334
|
+
.description('Start the MCP server on stdio (default) or HTTP (--port)')
|
|
335
|
+
.option('--port <n>', 'Listen on HTTP instead of stdio (Streamable HTTP transport)')
|
|
336
|
+
.action(async (options) => {
|
|
337
|
+
try {
|
|
338
|
+
if (options.port) {
|
|
339
|
+
const port = Number(options.port);
|
|
340
|
+
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
|
341
|
+
const msg = `Invalid --port "${options.port}". Must be 1-65535.`;
|
|
342
|
+
if (isJsonMode()) {
|
|
343
|
+
console.error(JSON.stringify({ error: { code: 2, kind: 'usage', message: msg } }));
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
console.error(msg);
|
|
347
|
+
}
|
|
348
|
+
process.exit(2);
|
|
349
|
+
}
|
|
350
|
+
const { createServer } = await import('node:http');
|
|
351
|
+
const httpServer = createServer(async (req, res) => {
|
|
352
|
+
// Stateless mode: fresh transport+server per request (SDK requirement).
|
|
353
|
+
const reqTransport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
354
|
+
const reqServer = createSwitchBotMcpServer();
|
|
355
|
+
// Register cleanup before any async work so it fires on both normal
|
|
356
|
+
// close and error-path close (after the 500 response ends).
|
|
357
|
+
res.on('close', () => {
|
|
358
|
+
reqTransport.close();
|
|
359
|
+
reqServer.close();
|
|
360
|
+
});
|
|
361
|
+
try {
|
|
362
|
+
await reqServer.connect(reqTransport);
|
|
363
|
+
await reqTransport.handleRequest(req, res);
|
|
364
|
+
}
|
|
365
|
+
catch (err) {
|
|
366
|
+
if (!res.headersSent) {
|
|
367
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
368
|
+
res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal server error' }, id: null }));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
httpServer.listen(port, () => {
|
|
373
|
+
console.error(`SwitchBot MCP server listening on http://localhost:${port}/mcp`);
|
|
374
|
+
});
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const server = createSwitchBotMcpServer();
|
|
378
|
+
const transport = new StdioServerTransport();
|
|
379
|
+
await server.connect(transport);
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
handleError(error);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export interface PlanCommandStep {
|
|
3
|
+
type: 'command';
|
|
4
|
+
deviceId?: string;
|
|
5
|
+
deviceName?: string;
|
|
6
|
+
command: string;
|
|
7
|
+
parameter?: unknown;
|
|
8
|
+
commandType?: 'command' | 'customize';
|
|
9
|
+
note?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PlanSceneStep {
|
|
12
|
+
type: 'scene';
|
|
13
|
+
sceneId: string;
|
|
14
|
+
note?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface PlanWaitStep {
|
|
17
|
+
type: 'wait';
|
|
18
|
+
ms: number;
|
|
19
|
+
note?: string;
|
|
20
|
+
}
|
|
21
|
+
export type PlanStep = PlanCommandStep | PlanSceneStep | PlanWaitStep;
|
|
22
|
+
export interface Plan {
|
|
23
|
+
version: '1.0';
|
|
24
|
+
description?: string;
|
|
25
|
+
steps: PlanStep[];
|
|
26
|
+
}
|
|
27
|
+
export interface PlanValidationIssue {
|
|
28
|
+
path: string;
|
|
29
|
+
message: string;
|
|
30
|
+
}
|
|
31
|
+
export declare function validatePlan(raw: unknown): {
|
|
32
|
+
ok: true;
|
|
33
|
+
plan: Plan;
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
issues: PlanValidationIssue[];
|
|
37
|
+
};
|
|
38
|
+
export declare function registerPlanCommand(program: Command): void;
|