@coffeerunhobby/mcp-ha-connect 2.2.1 → 2.4.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.
Files changed (293) hide show
  1. package/.env.example +1 -1
  2. package/README.md +24 -2
  3. package/dist/omadaClient/accessControl.d.ts +290 -0
  4. package/dist/omadaClient/accessControl.js +509 -0
  5. package/dist/omadaClient/accessControl.js.map +1 -0
  6. package/dist/omadaClient/client.d.ts +6 -1
  7. package/dist/omadaClient/client.js +12 -1
  8. package/dist/omadaClient/client.js.map +1 -1
  9. package/dist/omadaClient/diagnose.d.ts +68 -0
  10. package/dist/omadaClient/diagnose.js +164 -0
  11. package/dist/omadaClient/diagnose.js.map +1 -0
  12. package/dist/omadaClient/index.d.ts +9 -0
  13. package/dist/omadaClient/index.js +19 -0
  14. package/dist/omadaClient/index.js.map +1 -1
  15. package/dist/omadaClient/macGroups.d.ts +58 -0
  16. package/dist/omadaClient/macGroups.js +110 -0
  17. package/dist/omadaClient/macGroups.js.map +1 -0
  18. package/dist/omadaClient/serialize.d.ts +13 -0
  19. package/dist/omadaClient/serialize.js +29 -0
  20. package/dist/omadaClient/serialize.js.map +1 -0
  21. package/dist/omadaClient/timeRange.d.ts +41 -0
  22. package/dist/omadaClient/timeRange.js +109 -0
  23. package/dist/omadaClient/timeRange.js.map +1 -0
  24. package/dist/tools/infra/actions.d.ts +1 -1
  25. package/dist/tools/infra/actions.js +1 -1
  26. package/dist/tools/omada/accessControl.d.ts +151 -0
  27. package/dist/tools/omada/accessControl.js +158 -0
  28. package/dist/tools/omada/accessControl.js.map +1 -0
  29. package/dist/tools/omada/index.js +9 -0
  30. package/dist/tools/omada/index.js.map +1 -1
  31. package/dist/tools/omada/macGroups.d.ts +19 -0
  32. package/dist/tools/omada/macGroups.js +77 -0
  33. package/dist/tools/omada/macGroups.js.map +1 -0
  34. package/dist/tools/omada/namespace.js +16 -0
  35. package/dist/tools/omada/namespace.js.map +1 -1
  36. package/dist/version.d.ts +1 -1
  37. package/dist/version.js +1 -1
  38. package/package.json +5 -1
  39. package/.codex/review-rules.md +0 -16
  40. package/.dockerignore +0 -8
  41. package/.gitattributes +0 -30
  42. package/.github/workflows/ci.yml +0 -193
  43. package/AGENTS.md +0 -389
  44. package/Dockerfile +0 -50
  45. package/docker-compose.yml +0 -23
  46. package/docs/CHANGELOG.md +0 -341
  47. package/docs/COVERAGE.md +0 -219
  48. package/docs/QUICK_START.md +0 -240
  49. package/eslint.config.js +0 -33
  50. package/n8n/ai-agent-safety-monitor.json +0 -355
  51. package/openapi.json +0 -1259
  52. package/scripts/check-version-sync.mjs +0 -69
  53. package/scripts/generate-jwt.ts +0 -121
  54. package/src/config.ts +0 -440
  55. package/src/haClient/automations.ts +0 -261
  56. package/src/haClient/calendars.ts +0 -94
  57. package/src/haClient/config.ts +0 -52
  58. package/src/haClient/devices.ts +0 -653
  59. package/src/haClient/entities.ts +0 -128
  60. package/src/haClient/events.ts +0 -689
  61. package/src/haClient/history.ts +0 -73
  62. package/src/haClient/index.ts +0 -336
  63. package/src/haClient/request.ts +0 -142
  64. package/src/haClient/services.ts +0 -45
  65. package/src/haClient/states.ts +0 -60
  66. package/src/haClient/updates.ts +0 -58
  67. package/src/haClient/wsCommand.ts +0 -44
  68. package/src/index.ts +0 -164
  69. package/src/localAI/index.ts +0 -88
  70. package/src/localAI/prompts.ts +0 -50
  71. package/src/localAI/providers/base.ts +0 -120
  72. package/src/localAI/providers/ollama.ts +0 -78
  73. package/src/localAI/providers/openai.ts +0 -86
  74. package/src/localAI/types.ts +0 -67
  75. package/src/omadaClient/auth.ts +0 -178
  76. package/src/omadaClient/client.ts +0 -337
  77. package/src/omadaClient/device.ts +0 -118
  78. package/src/omadaClient/index.ts +0 -310
  79. package/src/omadaClient/network.ts +0 -284
  80. package/src/omadaClient/request.ts +0 -365
  81. package/src/omadaClient/security.ts +0 -51
  82. package/src/omadaClient/site.ts +0 -344
  83. package/src/ownerQuestions/service.ts +0 -536
  84. package/src/permissions/index.ts +0 -143
  85. package/src/resources/index.ts +0 -213
  86. package/src/server/auth.ts +0 -175
  87. package/src/server/chatFace.ts +0 -789
  88. package/src/server/chatSlice.ts +0 -57
  89. package/src/server/common.ts +0 -170
  90. package/src/server/eventSubscription.ts +0 -343
  91. package/src/server/http.ts +0 -626
  92. package/src/server/instructions.ts +0 -97
  93. package/src/server/rateLimiter.ts +0 -227
  94. package/src/server/stdio.ts +0 -41
  95. package/src/server/stream.ts +0 -205
  96. package/src/tools/ai/analyzeSensors.ts +0 -33
  97. package/src/tools/ai/index.ts +0 -20
  98. package/src/tools/common.ts +0 -442
  99. package/src/tools/homeassistant/askOwner.ts +0 -133
  100. package/src/tools/homeassistant/automationControls.ts +0 -66
  101. package/src/tools/homeassistant/calendar.ts +0 -97
  102. package/src/tools/homeassistant/callService.ts +0 -25
  103. package/src/tools/homeassistant/controlClimate.ts +0 -68
  104. package/src/tools/homeassistant/controlCover.ts +0 -48
  105. package/src/tools/homeassistant/controlFan.ts +0 -44
  106. package/src/tools/homeassistant/controlLight.ts +0 -38
  107. package/src/tools/homeassistant/controlMediaPlayer.ts +0 -50
  108. package/src/tools/homeassistant/createAutomation.ts +0 -35
  109. package/src/tools/homeassistant/deleteAutomation.ts +0 -24
  110. package/src/tools/homeassistant/entityAction.ts +0 -35
  111. package/src/tools/homeassistant/entityGuard.ts +0 -28
  112. package/src/tools/homeassistant/getAllSensors.ts +0 -46
  113. package/src/tools/homeassistant/getAutomationTrace.ts +0 -30
  114. package/src/tools/homeassistant/getDomainSummary.ts +0 -29
  115. package/src/tools/homeassistant/getEntitiesByDomain.ts +0 -56
  116. package/src/tools/homeassistant/getHistory.ts +0 -24
  117. package/src/tools/homeassistant/getState.ts +0 -24
  118. package/src/tools/homeassistant/getStates.ts +0 -46
  119. package/src/tools/homeassistant/getVersion.ts +0 -21
  120. package/src/tools/homeassistant/index.ts +0 -121
  121. package/src/tools/homeassistant/listAutomations.ts +0 -35
  122. package/src/tools/homeassistant/listEntities.ts +0 -44
  123. package/src/tools/homeassistant/listPersons.ts +0 -46
  124. package/src/tools/homeassistant/sceneAndScript.ts +0 -53
  125. package/src/tools/homeassistant/searchEntities.ts +0 -58
  126. package/src/tools/homeassistant/sendNotification.ts +0 -179
  127. package/src/tools/homeassistant/systemTools.ts +0 -52
  128. package/src/tools/homeassistant/triggerAutomation.ts +0 -24
  129. package/src/tools/index.ts +0 -78
  130. package/src/tools/infra/actions.ts +0 -95
  131. package/src/tools/infra/index.ts +0 -138
  132. package/src/tools/omada/blockClient.ts +0 -28
  133. package/src/tools/omada/cyclePoePort.ts +0 -32
  134. package/src/tools/omada/deleteClient.ts +0 -27
  135. package/src/tools/omada/disableClientRateLimit.ts +0 -23
  136. package/src/tools/omada/getClient.ts +0 -14
  137. package/src/tools/omada/getDevice.ts +0 -14
  138. package/src/tools/omada/getFirewallSetting.ts +0 -14
  139. package/src/tools/omada/getInternetInfo.ts +0 -14
  140. package/src/tools/omada/getLanNetworkList.ts +0 -14
  141. package/src/tools/omada/getLanProfileList.ts +0 -14
  142. package/src/tools/omada/getPortForwardingStatus.ts +0 -26
  143. package/src/tools/omada/getRateLimitProfiles.ts +0 -14
  144. package/src/tools/omada/getSiteNtpStatus.ts +0 -14
  145. package/src/tools/omada/getSsidDetail.ts +0 -22
  146. package/src/tools/omada/getSsidList.ts +0 -21
  147. package/src/tools/omada/getSwitchStackDetail.ts +0 -14
  148. package/src/tools/omada/getThreatList.ts +0 -42
  149. package/src/tools/omada/getWlanGroupList.ts +0 -14
  150. package/src/tools/omada/graph.ts +0 -187
  151. package/src/tools/omada/index.ts +0 -153
  152. package/src/tools/omada/listBlockedClients.ts +0 -17
  153. package/src/tools/omada/listClients.ts +0 -14
  154. package/src/tools/omada/listClientsActivity.ts +0 -25
  155. package/src/tools/omada/listClientsPastConnections.ts +0 -44
  156. package/src/tools/omada/listDevices.ts +0 -14
  157. package/src/tools/omada/listDevicesStats.ts +0 -41
  158. package/src/tools/omada/listMostActiveClients.ts +0 -14
  159. package/src/tools/omada/listSites.ts +0 -18
  160. package/src/tools/omada/namespace.ts +0 -1164
  161. package/src/tools/omada/searchDevices.ts +0 -20
  162. package/src/tools/omada/setClientRateLimit.ts +0 -26
  163. package/src/tools/omada/setClientRateLimitProfile.ts +0 -25
  164. package/src/tools/omada/setSiteNtpServers.ts +0 -36
  165. package/src/tools/omada/setSsidEnabled.ts +0 -38
  166. package/src/tools/omada/unblockClient.ts +0 -26
  167. package/src/tools/types.ts +0 -11
  168. package/src/tools/utils/pagination-schema.ts +0 -27
  169. package/src/types/ai/index.ts +0 -11
  170. package/src/types/ai/types.ts +0 -67
  171. package/src/types/homeassistant/automations.ts +0 -43
  172. package/src/types/homeassistant/calendars.ts +0 -26
  173. package/src/types/homeassistant/config.ts +0 -14
  174. package/src/types/homeassistant/entities.ts +0 -14
  175. package/src/types/homeassistant/errors.ts +0 -33
  176. package/src/types/homeassistant/haConfig.ts +0 -7
  177. package/src/types/homeassistant/history.ts +0 -13
  178. package/src/types/homeassistant/index.ts +0 -46
  179. package/src/types/homeassistant/request.ts +0 -6
  180. package/src/types/homeassistant/serviceCall.ts +0 -18
  181. package/src/types/homeassistant/states.ts +0 -58
  182. package/src/types/homeassistant/updates.ts +0 -28
  183. package/src/types/index.ts +0 -12
  184. package/src/types/omada/activeClientInfo.ts +0 -17
  185. package/src/types/omada/clientActivity.ts +0 -32
  186. package/src/types/omada/clientBlockStatus.ts +0 -11
  187. package/src/types/omada/clientPastConnection.ts +0 -82
  188. package/src/types/omada/index.ts +0 -14
  189. package/src/types/omada/omadaApiResponse.ts +0 -5
  190. package/src/types/omada/omadaClientInfo.ts +0 -11
  191. package/src/types/omada/omadaDeviceInfo.ts +0 -11
  192. package/src/types/omada/omadaDeviceStats.ts +0 -31
  193. package/src/types/omada/omadaSiteSummary.ts +0 -5
  194. package/src/types/omada/oswStackDetail.ts +0 -11
  195. package/src/types/omada/paginatedResult.ts +0 -6
  196. package/src/types/omada/rateLimitProfile.ts +0 -58
  197. package/src/types/omada/threatInfo.ts +0 -38
  198. package/src/types/omada/tokenResult.ts +0 -6
  199. package/src/types.ts +0 -2
  200. package/src/utils/config-validations.ts +0 -68
  201. package/src/utils/jwt.ts +0 -112
  202. package/src/utils/logger.ts +0 -121
  203. package/src/utils/plainText.ts +0 -48
  204. package/src/utils/redact.ts +0 -59
  205. package/src/utils/sanitizeError.ts +0 -18
  206. package/src/utils/tlsDispatcher.ts +0 -69
  207. package/src/version.ts +0 -5
  208. package/tests/bdd/automation-creation.spec.ts +0 -214
  209. package/tests/bdd/automation-management.spec.ts +0 -267
  210. package/tests/bdd/calendar.spec.ts +0 -190
  211. package/tests/bdd/configuration.spec.ts +0 -283
  212. package/tests/bdd/device-control.spec.ts +0 -344
  213. package/tests/bdd/entity-listing.spec.ts +0 -208
  214. package/tests/bdd/entity-search.spec.ts +0 -183
  215. package/tests/bdd/history-retrieval.spec.ts +0 -195
  216. package/tests/bdd/notifications.spec.ts +0 -238
  217. package/tests/bdd/service-calls.spec.ts +0 -144
  218. package/tests/bdd/state-retrieval.spec.ts +0 -204
  219. package/tests/bench/hotpaths.bench.ts +0 -68
  220. package/tests/config.test.ts +0 -355
  221. package/tests/features/automation-creation.feature +0 -36
  222. package/tests/features/automation-management.feature +0 -54
  223. package/tests/features/calendar.feature +0 -37
  224. package/tests/features/configuration.feature +0 -70
  225. package/tests/features/device-control.feature +0 -100
  226. package/tests/features/entity-listing.feature +0 -51
  227. package/tests/features/entity-search.feature +0 -44
  228. package/tests/features/history-retrieval.feature +0 -38
  229. package/tests/features/notifications.feature +0 -37
  230. package/tests/features/service-calls.feature +0 -35
  231. package/tests/features/state-retrieval.feature +0 -37
  232. package/tests/fixtures/openapi-default-slice.json +0 -290
  233. package/tests/haClient/automationTrace.test.ts +0 -59
  234. package/tests/haClient/automations.test.ts +0 -149
  235. package/tests/haClient/config.test.ts +0 -117
  236. package/tests/haClient/entities.test.ts +0 -188
  237. package/tests/haClient/events.test.ts +0 -455
  238. package/tests/haClient/eventsTls.test.ts +0 -52
  239. package/tests/haClient/history.test.ts +0 -228
  240. package/tests/haClient/index.test.ts +0 -243
  241. package/tests/haClient/request.test.ts +0 -217
  242. package/tests/haClient/services.test.ts +0 -120
  243. package/tests/haClient/states.test.ts +0 -119
  244. package/tests/haClient/updates.test.ts +0 -193
  245. package/tests/index.test.ts +0 -160
  246. package/tests/integration/homeassistant.test.ts +0 -144
  247. package/tests/integration/omada.test.ts +0 -167
  248. package/tests/integration/server.test.ts +0 -151
  249. package/tests/localAI/index.test.ts +0 -177
  250. package/tests/localAI/ollama.test.ts +0 -187
  251. package/tests/localAI/openai.test.ts +0 -257
  252. package/tests/omadaClient/actions.test.ts +0 -161
  253. package/tests/omadaClient/clientBlocks.test.ts +0 -170
  254. package/tests/omadaClient/ntp.test.ts +0 -381
  255. package/tests/omadaClient/site.test.ts +0 -37
  256. package/tests/ownerQuestions/service.test.ts +0 -499
  257. package/tests/ownerQuestions/tools.test.ts +0 -186
  258. package/tests/permissions/index.test.ts +0 -217
  259. package/tests/resources/index.test.ts +0 -288
  260. package/tests/scripts/generateJwt.test.ts +0 -44
  261. package/tests/security/accessControl.test.ts +0 -223
  262. package/tests/security/authentication.test.ts +0 -117
  263. package/tests/security/configuration.test.ts +0 -170
  264. package/tests/security/cryptoTransport.test.ts +0 -172
  265. package/tests/security/dependencies.test.ts +0 -152
  266. package/tests/security/injection.test.ts +0 -117
  267. package/tests/security/insecureDesign.test.ts +0 -121
  268. package/tests/security/logging.test.ts +0 -131
  269. package/tests/security/resourceConsumption.test.ts +0 -142
  270. package/tests/server/auth.test.ts +0 -249
  271. package/tests/server/chatFace.test.ts +0 -320
  272. package/tests/server/common.test.ts +0 -84
  273. package/tests/server/http.test.ts +0 -561
  274. package/tests/server/instructions.test.ts +0 -152
  275. package/tests/server/memoizeJsonSchema.test.ts +0 -71
  276. package/tests/server/stdio.test.ts +0 -99
  277. package/tests/server/stream.test.ts +0 -285
  278. package/tests/server/toolVisibility.http.test.ts +0 -111
  279. package/tests/server/toolVisibility.test.ts +0 -129
  280. package/tests/tools/ai/analyzeSensors.test.ts +0 -227
  281. package/tests/tools/common.test.ts +0 -123
  282. package/tests/tools/homeassistant/handlers.test.ts +0 -872
  283. package/tests/tools/infra/invokeAction.test.ts +0 -280
  284. package/tests/tools/omada/graph.test.ts +0 -385
  285. package/tests/tools/omada/handlers.test.ts +0 -897
  286. package/tests/tools/omada/namespace.test.ts +0 -483
  287. package/tests/tools/registry.test.ts +0 -168
  288. package/tests/utils/config-validations.test.ts +0 -352
  289. package/tests/utils/jwt.test.ts +0 -115
  290. package/tests/utils/logger.test.ts +0 -327
  291. package/tests/utils/redact.test.ts +0 -105
  292. package/tsconfig.json +0 -24
  293. package/vitest.config.ts +0 -38
@@ -1,22 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import { z } from 'zod';
3
-
4
- import type { OmadaClient } from '../../omadaClient/index.js';
5
- import { toToolResult, wrapToolHandler, Permission } from '../common.js';
6
-
7
- const ssidDetailSchema = z.object({
8
- wlanId: z.string().min(1, 'wlanId is required. Use getWlanGroupList to get available WLAN group IDs.'),
9
- ssidId: z.string().min(1, 'ssidId is required. Use getSsidList to get available SSID IDs.'),
10
- siteId: z.string().min(1).optional(),
11
- });
12
-
13
- export function registerGetSsidDetailTool(server: McpServer, client: OmadaClient): void {
14
- server.registerTool(
15
- 'omada_getSsidDetail',
16
- {
17
- description: 'Get detailed SSID configuration',
18
- inputSchema: ssidDetailSchema,
19
- },
20
- wrapToolHandler('omada_getSsidDetail', async ({ wlanId, ssidId, siteId }) => toToolResult(await client.getSsidDetail(wlanId, ssidId, siteId)), Permission.QUERY)
21
- );
22
- }
@@ -1,21 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import { z } from 'zod';
3
-
4
- import type { OmadaClient } from '../../omadaClient/index.js';
5
- import { toToolResult, wrapToolHandler, Permission } from '../common.js';
6
-
7
- const ssidListSchema = z.object({
8
- wlanId: z.string().min(1, 'wlanId is required. Use getWlanGroupList to get available WLAN group IDs.'),
9
- siteId: z.string().min(1).optional(),
10
- });
11
-
12
- export function registerGetSsidListTool(server: McpServer, client: OmadaClient): void {
13
- server.registerTool(
14
- 'omada_getSsidList',
15
- {
16
- description: 'Get SSID list for a WLAN group',
17
- inputSchema: ssidListSchema,
18
- },
19
- wrapToolHandler('omada_getSsidList', async ({ wlanId, siteId }) => toToolResult(await client.getSsidList(wlanId, siteId)), Permission.QUERY)
20
- );
21
- }
@@ -1,14 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import type { OmadaClient } from '../../omadaClient/index.js';
3
- import { stackIdSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
4
-
5
- export function registerGetSwitchStackDetailTool(server: McpServer, client: OmadaClient): void {
6
- server.registerTool(
7
- 'omada_getSwitchStackDetail',
8
- {
9
- description: 'Get detailed information about a switch stack',
10
- inputSchema: stackIdSchema,
11
- },
12
- wrapToolHandler('omada_getSwitchStackDetail', async ({ stackId, siteId }) => toToolResult(await client.getSwitchStackDetail(stackId, siteId)), Permission.QUERY)
13
- );
14
- }
@@ -1,42 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import { z } from 'zod';
3
-
4
- import type { OmadaClient } from '../../omadaClient/index.js';
5
- import { toToolResult, wrapToolHandler, Permission } from '../common.js';
6
- import { createPaginationSchema } from '../utils/pagination-schema.js';
7
-
8
- const getThreatListSchema = z.object({
9
- siteList: z.string().optional().describe('Comma-separated site IDs. If not provided, all sites are selected by default.'),
10
- archived: z.boolean().describe('Whether to include archived threats'),
11
- ...createPaginationSchema(10),
12
- startTime: z.number().int().describe('Start timestamp in seconds (e.g., 1682000000)'),
13
- endTime: z.number().int().describe('End timestamp in seconds (e.g., 1682000000)'),
14
- severity: z.number().int().min(0).max(3).optional().describe('Threat severity: 0=Critical, 1=Major, 2=Concerning, 3=Minor'),
15
- sortTime: z.enum(['asc', 'desc']).optional().describe('Sort by time: asc or desc'),
16
- searchKey: z.string().optional().describe('Fuzzy search for Threat Description/Classification/Classification Description'),
17
- });
18
-
19
- export function registerGetThreatListTool(server: McpServer, client: OmadaClient): void {
20
- server.registerTool(
21
- 'omada_getThreatList',
22
- {
23
- description: 'Get security threat management list',
24
- inputSchema: getThreatListSchema,
25
- },
26
- wrapToolHandler('omada_getThreatList', async (args) => {
27
- const options = {
28
- siteList: args.siteList,
29
- archived: args.archived,
30
- page: args.page,
31
- pageSize: args.pageSize,
32
- startTime: args.startTime,
33
- endTime: args.endTime,
34
- severity: args.severity,
35
- sortTime: args.sortTime,
36
- searchKey: args.searchKey,
37
- };
38
-
39
- return toToolResult(await client.getThreatList(options));
40
- }, Permission.ADMIN)
41
- );
42
- }
@@ -1,14 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import type { OmadaClient } from '../../omadaClient/index.js';
3
- import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
4
-
5
- export function registerGetWlanGroupListTool(server: McpServer, client: OmadaClient): void {
6
- server.registerTool(
7
- 'omada_getWlanGroupList',
8
- {
9
- description: 'Get WLAN group configuration list',
10
- inputSchema: siteInputSchema,
11
- },
12
- wrapToolHandler('omada_getWlanGroupList', async ({ siteId }) => toToolResult(await client.getWlanGroupList(siteId)), Permission.QUERY)
13
- );
14
- }
@@ -1,187 +0,0 @@
1
- /**
2
- * Omada resource-graph tools: `omada_browse` + `omada_read`.
3
- *
4
- * These two tools replace dozens of per-endpoint getters when
5
- * `MCP_TOOL_REGISTRATION_MODE=graph`. The model navigates a discoverable
6
- * namespace (browse → read) instead of selecting from a flat list of ~25 tool
7
- * schemas, which keeps the tool-schema budget small for low-context models while
8
- * still exposing every read endpoint.
9
- *
10
- * Authorization mirrors `wrapToolHandler`:
11
- * - `omada_browse` requires QUERY to use, and additionally filters the children
12
- * it advertises by the caller's mask (so sensitive subtrees stay invisible).
13
- * - `omada_read` declares NO static tool permission; instead it enforces the
14
- * per-path permission from the manifest, so each path can require a different
15
- * bit. Unknown / container / under-privileged paths fail closed.
16
- */
17
-
18
- import type { McpServer } from '@modelcontextprotocol/server';
19
- import { z } from 'zod';
20
- import type { OmadaClient } from '../../omadaClient/index.js';
21
- import { hasPermission, getPermissionNames } from '../../permissions/index.js';
22
- import { toToolResult, wrapToolHandler, getCallerPermissions, setToolVisibilityRule, Permission, type ToolExtra } from '../common.js';
23
- import { OMADA_RESOURCES, childrenOf, getResourceNode, normalizePath, type ReadArgs, type ResourceNode } from './namespace.js';
24
-
25
- /** Compact, model-friendly metadata for a node (used in browse output). */
26
- function nodeSummary(node: ResourceNode): Record<string, unknown> {
27
- return {
28
- path: node.path,
29
- kind: node.kind,
30
- permission: getPermissionNames(node.permission).join(',') || 'NONE',
31
- description: node.description,
32
- readable: node.fetch !== undefined,
33
- ...(node.estimatedSize ? { estimatedSize: node.estimatedSize } : {}),
34
- ...(node.paginated ? { supportsPagination: true, defaultPageSize: node.defaultPageSize ?? 50 } : {}),
35
- ...(node.params && node.params.length > 0 ? { params: node.params } : {}),
36
- };
37
- }
38
-
39
- export const browseSchema = z.object({
40
- path: z
41
- .string()
42
- .default('/')
43
- .describe('Resource path to explore (default "/"). e.g. "/", "/gateway", "/wifi", "/network".'),
44
- });
45
-
46
- export const readSchema = z.object({
47
- path: z.string().describe('Resource path discovered via omada_browse, e.g. "/gateway/wan", "/clients", "/events".'),
48
- siteId: z.string().min(1).optional().describe('Site ID (optional; uses the default site if not set).'),
49
- id: z.string().min(1).optional().describe('Look up a single member of a collection by id/MAC (where supported).'),
50
- params: z
51
- .record(z.string(), z.string())
52
- .optional()
53
- .describe('Path parameters some resources require (see omada_browse "params"), e.g. {"gatewayMac":"AA-BB-CC-DD-EE-FF"}.'),
54
- page: z.number().int().min(1).optional().describe('Page number for paginated resources (e.g. "/events").'),
55
- pageSize: z.number().int().min(1).max(200).optional().describe('Page size for paginated resources (max 200).'),
56
- });
57
-
58
- /**
59
- * Shared `omada_browse` handler — used by both the MCP registration below and
60
- * the chat face (`POST /api/tools/omada_browse`), so validation, RBAC, and
61
- * behavior stay identical on both faces (v1.7 single-source-of-truth rule).
62
- */
63
- export function createBrowseHandler(client: OmadaClient) {
64
- void client; // browse only walks the static manifest; client kept for signature symmetry
65
- return wrapToolHandler(
66
- 'omada_browse',
67
- async ({ path }: z.infer<typeof browseSchema>, extra: ToolExtra): Promise<ReturnType<typeof toToolResult>> => {
68
- const norm = normalizePath(path);
69
- const node = getResourceNode(norm);
70
- if (!node) {
71
- return toToolResult(
72
- { error: 'Unknown path', message: `No Omada resource at '${norm}'. Start at '/' and browse downward.` },
73
- true
74
- );
75
- }
76
- const mask = getCallerPermissions(extra);
77
- const children = childrenOf(norm)
78
- .filter((child) => hasPermission(mask, child.permission))
79
- .map(nodeSummary);
80
- return toToolResult({
81
- path: norm,
82
- kind: node.kind,
83
- description: node.description,
84
- readable: node.fetch !== undefined,
85
- ...(node.params && node.params.length > 0 ? { params: node.params } : {}),
86
- ...(node.paginated ? { supportsPagination: true, defaultPageSize: node.defaultPageSize ?? 50 } : {}),
87
- children,
88
- });
89
- },
90
- Permission.QUERY
91
- );
92
- }
93
-
94
- /**
95
- * `omada_read` is usable iff the caller holds the permission of at least one readable
96
- * (fetchable) resource node; otherwise it is hidden from `tools/list`. Kept next to the
97
- * handler so the rule cannot drift from the per-path check it summarises.
98
- */
99
- export function canReadAnyResource(callerPermissions: number): boolean {
100
- return OMADA_RESOURCES.some((node) => node.fetch !== undefined && hasPermission(callerPermissions, node.permission));
101
- }
102
-
103
- /** Shared `omada_read` handler — same dual-face reuse as createBrowseHandler. */
104
- export function createReadHandler(client: OmadaClient) {
105
- setToolVisibilityRule('omada_read', canReadAnyResource);
106
- // No static permission: each path declares its own, enforced below (fail-closed).
107
- return wrapToolHandler(
108
- 'omada_read',
109
- async (args: z.infer<typeof readSchema>, extra: ToolExtra): Promise<ReturnType<typeof toToolResult>> => {
110
- const norm = normalizePath(args.path);
111
- const node = getResourceNode(norm);
112
- if (!node) {
113
- return toToolResult(
114
- { error: 'Unknown path', message: `No Omada resource at '${norm}'. Use omada_browse to discover valid paths.` },
115
- true
116
- );
117
- }
118
- if (!node.fetch) {
119
- return toToolResult(
120
- { error: 'Not readable', message: `'${norm}' is a container. Use omada_browse to list its children.` },
121
- true
122
- );
123
- }
124
-
125
- const mask = getCallerPermissions(extra);
126
- if (!hasPermission(mask, node.permission)) {
127
- const required = getPermissionNames(node.permission);
128
- const has = getPermissionNames(mask);
129
- return toToolResult(
130
- { error: 'Permission denied', message: `Reading '${norm}' requires permission: ${required.join(', ')}`, required, has },
131
- true
132
- );
133
- }
134
-
135
- // Validate required path parameters before touching the controller.
136
- const missing = (node.params ?? [])
137
- .filter((p) => p.required && !args.params?.[p.name])
138
- .map((p) => p.name);
139
- if (missing.length > 0) {
140
- return toToolResult(
141
- { error: 'Missing parameters', message: `'${norm}' requires: ${missing.join(', ')}. Pass them in "params".`, missing },
142
- true
143
- );
144
- }
145
-
146
- const readArgs: ReadArgs = {
147
- siteId: args.siteId,
148
- id: args.id,
149
- params: args.params,
150
- page: args.page,
151
- pageSize: args.pageSize,
152
- };
153
- return toToolResult(await node.fetch(client, readArgs));
154
- }
155
- );
156
- }
157
-
158
- /**
159
- * Register the resource-graph tools. Returns the number of tools registered (2).
160
- */
161
- export function registerOmadaGraphTools(server: McpServer, client: OmadaClient): number {
162
- server.registerTool(
163
- 'omada_browse',
164
- {
165
- description:
166
- 'Discover the Omada network resource graph. Returns the children and metadata at a path (resource TYPES, not ' +
167
- 'instances — no MAC enumeration), so navigate from "/" downward, then call omada_read at a readable node to fetch ' +
168
- 'data. This replaces dozens of individual getters with one discoverable namespace.',
169
- inputSchema: browseSchema,
170
- },
171
- createBrowseHandler(client)
172
- );
173
-
174
- server.registerTool(
175
- 'omada_read',
176
- {
177
- description:
178
- 'Read data from an Omada resource path discovered via omada_browse. Handles single resources (e.g. "/gateway/wan"), ' +
179
- 'collections (e.g. "/clients", optionally id=<MAC> for one member), and paginated logs (e.g. "/events" with ' +
180
- 'page/pageSize). Authorization is enforced per-path.',
181
- inputSchema: readSchema,
182
- },
183
- createReadHandler(client)
184
- );
185
-
186
- return 2;
187
- }
@@ -1,153 +0,0 @@
1
- /**
2
- * Omada tools index - registers all Omada MCP tools
3
- */
4
-
5
- import type { McpServer } from '@modelcontextprotocol/server';
6
- import type { OmadaClient } from '../../omadaClient/index.js';
7
- import { logger } from '../../utils/logger.js';
8
-
9
- import { registerBlockClientTool } from './blockClient.js';
10
- import { registerDeleteClientTool } from './deleteClient.js';
11
- import { registerListBlockedClientsTool } from './listBlockedClients.js';
12
- import { registerCyclePoePortTool } from './cyclePoePort.js';
13
- import { registerOmadaGraphTools } from './graph.js';
14
- import { registerSetSiteNtpServersTool } from './setSiteNtpServers.js';
15
- import { registerSetSsidEnabledTool } from './setSsidEnabled.js';
16
- import { registerDisableClientRateLimitTool } from './disableClientRateLimit.js';
17
- import { registerGetClientTool } from './getClient.js';
18
- import { registerGetDeviceTool } from './getDevice.js';
19
- import { registerGetFirewallSettingTool } from './getFirewallSetting.js';
20
- import { registerGetInternetInfoTool } from './getInternetInfo.js';
21
- import { registerGetLanNetworkListTool } from './getLanNetworkList.js';
22
- import { registerGetLanProfileListTool } from './getLanProfileList.js';
23
- import { registerGetPortForwardingStatusTool } from './getPortForwardingStatus.js';
24
- import { registerGetRateLimitProfilesTool } from './getRateLimitProfiles.js';
25
- import { registerGetSiteNtpStatusTool } from './getSiteNtpStatus.js';
26
- import { registerGetSsidDetailTool } from './getSsidDetail.js';
27
- import { registerGetSsidListTool } from './getSsidList.js';
28
- import { registerGetSwitchStackDetailTool } from './getSwitchStackDetail.js';
29
- import { registerGetThreatListTool } from './getThreatList.js';
30
- import { registerGetWlanGroupListTool } from './getWlanGroupList.js';
31
- import { registerListClientsTool } from './listClients.js';
32
- import { registerListClientsActivityTool } from './listClientsActivity.js';
33
- import { registerListClientsPastConnectionsTool } from './listClientsPastConnections.js';
34
- import { registerListDevicesTool } from './listDevices.js';
35
- import { registerListDevicesStatsTool } from './listDevicesStats.js';
36
- import { registerListMostActiveClientsTool } from './listMostActiveClients.js';
37
- import { registerListSitesTool } from './listSites.js';
38
- import { registerSearchDevicesTool } from './searchDevices.js';
39
- import { registerSetClientRateLimitTool } from './setClientRateLimit.js';
40
- import { registerSetClientRateLimitProfileTool } from './setClientRateLimitProfile.js';
41
- import { registerUnblockClientTool } from './unblockClient.js';
42
-
43
- /**
44
- * Tool registration strategy for the Omada plugin.
45
- * - `eager` (default): register every typed Omada tool (~25). Backwards-compatible.
46
- * - `graph`: register only the resource-graph reads (`omada_browse` + `omada_read`)
47
- * plus the typed write/action tools. Keeps the tool-schema budget small for
48
- * low-context models while still exposing every read endpoint via the namespace.
49
- */
50
- export type OmadaRegistrationMode = 'eager' | 'graph';
51
-
52
- export function registerOmadaTools(
53
- server: McpServer,
54
- client: OmadaClient,
55
- mode: OmadaRegistrationMode = 'eager'
56
- ): number {
57
- if (mode === 'graph') {
58
- return registerOmadaToolsGraph(server, client);
59
- }
60
-
61
- logger.debug('Registering Omada tools', { mode });
62
- let toolCount = 0;
63
-
64
- // Site tools
65
- registerListSitesTool(server, client);
66
- registerGetSiteNtpStatusTool(server, client);
67
- registerSetSiteNtpServersTool(server, client);
68
- toolCount += 3;
69
-
70
- // Device tools
71
- registerListDevicesTool(server, client);
72
- registerGetDeviceTool(server, client);
73
- registerSearchDevicesTool(server, client);
74
- registerGetSwitchStackDetailTool(server, client);
75
- registerListDevicesStatsTool(server, client);
76
- toolCount += 5;
77
-
78
- // Client tools
79
- registerListClientsTool(server, client);
80
- registerGetClientTool(server, client);
81
- registerListMostActiveClientsTool(server, client);
82
- registerListClientsActivityTool(server, client);
83
- registerListClientsPastConnectionsTool(server, client);
84
- toolCount += 5;
85
-
86
- // Rate limit tools
87
- registerGetRateLimitProfilesTool(server, client);
88
- registerSetClientRateLimitTool(server, client);
89
- registerSetClientRateLimitProfileTool(server, client);
90
- registerDisableClientRateLimitTool(server, client);
91
- toolCount += 4;
92
-
93
- // Client block tools
94
- registerBlockClientTool(server, client);
95
- registerUnblockClientTool(server, client);
96
- registerListBlockedClientsTool(server, client);
97
- registerDeleteClientTool(server, client);
98
- toolCount += 4;
99
-
100
- // Device action tools
101
- registerCyclePoePortTool(server, client);
102
- toolCount += 1;
103
-
104
- // Security tools
105
- registerGetThreatListTool(server, client);
106
- toolCount += 1;
107
-
108
- // Network tools
109
- registerGetInternetInfoTool(server, client);
110
- registerGetPortForwardingStatusTool(server, client);
111
- registerGetLanNetworkListTool(server, client);
112
- registerGetLanProfileListTool(server, client);
113
- registerGetWlanGroupListTool(server, client);
114
- registerGetSsidListTool(server, client);
115
- registerGetSsidDetailTool(server, client);
116
- registerGetFirewallSettingTool(server, client);
117
- registerSetSsidEnabledTool(server, client);
118
- toolCount += 9;
119
-
120
- logger.info('Omada tools registered', { mode, toolCount });
121
- return toolCount;
122
- }
123
-
124
- /**
125
- * Graph mode: resource-graph reads + typed writes/actions only.
126
- *
127
- * Reads collapse into `omada_browse` + `omada_read` (see ./graph.ts and
128
- * ./namespace.ts). Writes stay as individual, explicitly permission-gated tools —
129
- * they mutate real network state, so each keeps its own narrow schema and RBAC
130
- * (CONTROL) rather than being funneled through a generic verb.
131
- */
132
- function registerOmadaToolsGraph(server: McpServer, client: OmadaClient): number {
133
- logger.debug('Registering Omada tools', { mode: 'graph' });
134
- let toolCount = 0;
135
-
136
- // Resource-graph reads (browse + read)
137
- toolCount += registerOmadaGraphTools(server, client);
138
-
139
- // Typed write / action tools (unchanged, individually permission-gated)
140
- registerSetClientRateLimitTool(server, client);
141
- registerSetClientRateLimitProfileTool(server, client);
142
- registerDisableClientRateLimitTool(server, client);
143
- registerBlockClientTool(server, client);
144
- registerUnblockClientTool(server, client);
145
- registerDeleteClientTool(server, client);
146
- registerCyclePoePortTool(server, client);
147
- registerSetSsidEnabledTool(server, client);
148
- registerSetSiteNtpServersTool(server, client);
149
- toolCount += 9;
150
-
151
- logger.info('Omada tools registered', { mode: 'graph', toolCount });
152
- return toolCount;
153
- }
@@ -1,17 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
-
3
- import type { OmadaClient } from '../../omadaClient/index.js';
4
- import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
5
-
6
- export function registerListBlockedClientsTool(server: McpServer, client: OmadaClient): void {
7
- server.registerTool(
8
- 'omada_listBlockedClients',
9
- {
10
- description:
11
- 'List clients that are currently blocked, including offline ones (from the known-clients list). ' +
12
- 'Use it to check block state before or after omada_blockClient / omada_unblockClient.',
13
- inputSchema: siteInputSchema,
14
- },
15
- wrapToolHandler('omada_listBlockedClients', async ({ siteId }) => toToolResult(await client.listBlockedClients(siteId)), Permission.QUERY)
16
- );
17
- }
@@ -1,14 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import type { OmadaClient } from '../../omadaClient/index.js';
3
- import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
4
-
5
- export function registerListClientsTool(server: McpServer, client: OmadaClient): void {
6
- server.registerTool(
7
- 'omada_listClients',
8
- {
9
- description: 'List all connected clients (devices/users) in a site',
10
- inputSchema: siteInputSchema,
11
- },
12
- wrapToolHandler('omada_listClients', async ({ siteId }) => toToolResult(await client.listClients(siteId)), Permission.QUERY)
13
- );
14
- }
@@ -1,25 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import { z } from 'zod';
3
-
4
- import type { OmadaClient } from '../../omadaClient/index.js';
5
- import { toToolResult, wrapToolHandler, Permission } from '../common.js';
6
-
7
- const clientActivityInputSchema = z.object({
8
- siteId: z.string().optional().describe('Optional site ID. If not provided, uses the default site from configuration.'),
9
- start: z.number().int().optional().describe('Optional start timestamp in seconds (e.g., 1682000000)'),
10
- end: z.number().int().optional().describe('Optional end timestamp in seconds (e.g., 1682000000)'),
11
- });
12
-
13
- export function registerListClientsActivityTool(server: McpServer, client: OmadaClient): void {
14
- server.registerTool(
15
- 'omada_listClientsActivity',
16
- {
17
- description: 'Get client activity statistics over time',
18
- inputSchema: clientActivityInputSchema,
19
- },
20
- wrapToolHandler('omada_listClientsActivity', async ({ siteId, start, end }) =>
21
- toToolResult(await client.listClientsActivity({ siteId, start, end })),
22
- Permission.QUERY
23
- )
24
- );
25
- }
@@ -1,44 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import { z } from 'zod';
3
-
4
- import type { OmadaClient } from '../../omadaClient/index.js';
5
- import { toToolResult, wrapToolHandler, Permission } from '../common.js';
6
- import { createPaginationSchema } from '../utils/pagination-schema.js';
7
-
8
- const clientsPastConnectionsInputSchema = z.object({
9
- siteId: z.string().optional().describe('Optional site ID. If not provided, uses the default site from configuration.'),
10
- ...createPaginationSchema(50),
11
- sortLastSeen: z
12
- .enum(['asc', 'desc'])
13
- .optional()
14
- .describe('Sort by last seen time. Values: asc or desc. When multiple sorts exist, first one takes effect.'),
15
- timeStart: z.number().int().optional().describe('Filter by time range start timestamp (milliseconds).'),
16
- timeEnd: z.number().int().optional().describe('Filter by time range end timestamp (milliseconds).'),
17
- guest: z.boolean().optional().describe('Filter by guest status (true/false).'),
18
- searchKey: z.string().optional().describe('Fuzzy search by name, MAC address, or SSID.'),
19
- });
20
-
21
- export function registerListClientsPastConnectionsTool(server: McpServer, client: OmadaClient): void {
22
- server.registerTool(
23
- 'omada_listClientsPastConnections',
24
- {
25
- description: 'Get historical client connection data',
26
- inputSchema: clientsPastConnectionsInputSchema,
27
- },
28
- wrapToolHandler('omada_listClientsPastConnections', async (args) =>
29
- toToolResult(
30
- await client.listClientsPastConnections({
31
- siteId: args.siteId,
32
- page: args.page,
33
- pageSize: args.pageSize,
34
- sortLastSeen: args.sortLastSeen,
35
- timeStart: args.timeStart,
36
- timeEnd: args.timeEnd,
37
- guest: args.guest,
38
- searchKey: args.searchKey,
39
- })
40
- ),
41
- Permission.QUERY
42
- )
43
- );
44
- }
@@ -1,14 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import type { OmadaClient } from '../../omadaClient/index.js';
3
- import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
4
-
5
- export function registerListDevicesTool(server: McpServer, client: OmadaClient): void {
6
- server.registerTool(
7
- 'omada_listDevices',
8
- {
9
- description: 'List all network devices (APs, switches, gateways) in a site',
10
- inputSchema: siteInputSchema,
11
- },
12
- wrapToolHandler('omada_listDevices', async ({ siteId }) => toToolResult(await client.listDevices(siteId)), Permission.QUERY)
13
- );
14
- }
@@ -1,41 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import { z } from 'zod';
3
-
4
- import type { OmadaClient } from '../../omadaClient/index.js';
5
- import { toToolResult, wrapToolHandler, Permission } from '../common.js';
6
- import { createPaginationSchema } from '../utils/pagination-schema.js';
7
-
8
- const listDevicesStatsSchema = z.object({
9
- ...createPaginationSchema(100),
10
- searchMacs: z.string().optional(),
11
- searchNames: z.string().optional(),
12
- searchModels: z.string().optional(),
13
- searchSns: z.string().optional(),
14
- filterTag: z.string().optional(),
15
- filterDeviceSeriesType: z.string().optional(),
16
- });
17
-
18
- export function registerListDevicesStatsTool(server: McpServer, client: OmadaClient): void {
19
- server.registerTool(
20
- 'omada_listDevicesStats',
21
- {
22
- description: 'Get statistics for global adopted devices with filtering',
23
- inputSchema: listDevicesStatsSchema,
24
- },
25
- wrapToolHandler('omada_listDevicesStats', async (args) =>
26
- toToolResult(
27
- await client.listDevicesStats({
28
- page: args.page,
29
- pageSize: args.pageSize,
30
- searchMacs: args.searchMacs,
31
- searchNames: args.searchNames,
32
- searchModels: args.searchModels,
33
- searchSns: args.searchSns,
34
- filterTag: args.filterTag,
35
- filterDeviceSeriesType: args.filterDeviceSeriesType,
36
- })
37
- ),
38
- Permission.QUERY
39
- )
40
- );
41
- }
@@ -1,14 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import type { OmadaClient } from '../../omadaClient/index.js';
3
- import { siteInputSchema, toToolResult, wrapToolHandler, Permission } from '../common.js';
4
-
5
- export function registerListMostActiveClientsTool(server: McpServer, client: OmadaClient): void {
6
- server.registerTool(
7
- 'omada_listMostActiveClients',
8
- {
9
- description: 'Get the most active clients sorted by total traffic',
10
- inputSchema: siteInputSchema,
11
- },
12
- wrapToolHandler('omada_listMostActiveClients', async ({ siteId }) => toToolResult(await client.listMostActiveClients(siteId)), Permission.QUERY)
13
- );
14
- }
@@ -1,18 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/server';
2
- import { z } from 'zod';
3
-
4
- import type { OmadaClient } from '../../omadaClient/index.js';
5
- import { toToolResult, wrapToolHandler, Permission } from '../common.js';
6
-
7
- const emptySchema = z.object({});
8
-
9
- export function registerListSitesTool(server: McpServer, client: OmadaClient): void {
10
- server.registerTool(
11
- 'omada_listSites',
12
- {
13
- description: 'List all sites configured on the Omada controller',
14
- inputSchema: emptySchema,
15
- },
16
- wrapToolHandler('omada_listSites', async () => toToolResult(await client.listSites()), Permission.QUERY)
17
- );
18
- }