@coffeerunhobby/mcp-ha-connect 2.2.1 → 2.3.1

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 (281) hide show
  1. package/.env.example +1 -1
  2. package/README.md +11 -2
  3. package/dist/omadaClient/client.d.ts +6 -1
  4. package/dist/omadaClient/client.js +12 -1
  5. package/dist/omadaClient/client.js.map +1 -1
  6. package/dist/omadaClient/diagnose.d.ts +68 -0
  7. package/dist/omadaClient/diagnose.js +164 -0
  8. package/dist/omadaClient/diagnose.js.map +1 -0
  9. package/dist/omadaClient/index.d.ts +6 -0
  10. package/dist/omadaClient/index.js +15 -0
  11. package/dist/omadaClient/index.js.map +1 -1
  12. package/dist/omadaClient/macGroups.d.ts +58 -0
  13. package/dist/omadaClient/macGroups.js +124 -0
  14. package/dist/omadaClient/macGroups.js.map +1 -0
  15. package/dist/tools/infra/actions.d.ts +1 -1
  16. package/dist/tools/infra/actions.js +1 -1
  17. package/dist/tools/omada/index.js +6 -0
  18. package/dist/tools/omada/index.js.map +1 -1
  19. package/dist/tools/omada/macGroups.d.ts +19 -0
  20. package/dist/tools/omada/macGroups.js +77 -0
  21. package/dist/tools/omada/macGroups.js.map +1 -0
  22. package/dist/tools/omada/namespace.js +8 -0
  23. package/dist/tools/omada/namespace.js.map +1 -1
  24. package/dist/version.d.ts +1 -1
  25. package/dist/version.js +1 -1
  26. package/package.json +5 -1
  27. package/.codex/review-rules.md +0 -16
  28. package/.dockerignore +0 -8
  29. package/.gitattributes +0 -30
  30. package/.github/workflows/ci.yml +0 -193
  31. package/AGENTS.md +0 -389
  32. package/Dockerfile +0 -50
  33. package/docker-compose.yml +0 -23
  34. package/docs/CHANGELOG.md +0 -341
  35. package/docs/COVERAGE.md +0 -219
  36. package/docs/QUICK_START.md +0 -240
  37. package/eslint.config.js +0 -33
  38. package/n8n/ai-agent-safety-monitor.json +0 -355
  39. package/openapi.json +0 -1259
  40. package/scripts/check-version-sync.mjs +0 -69
  41. package/scripts/generate-jwt.ts +0 -121
  42. package/src/config.ts +0 -440
  43. package/src/haClient/automations.ts +0 -261
  44. package/src/haClient/calendars.ts +0 -94
  45. package/src/haClient/config.ts +0 -52
  46. package/src/haClient/devices.ts +0 -653
  47. package/src/haClient/entities.ts +0 -128
  48. package/src/haClient/events.ts +0 -689
  49. package/src/haClient/history.ts +0 -73
  50. package/src/haClient/index.ts +0 -336
  51. package/src/haClient/request.ts +0 -142
  52. package/src/haClient/services.ts +0 -45
  53. package/src/haClient/states.ts +0 -60
  54. package/src/haClient/updates.ts +0 -58
  55. package/src/haClient/wsCommand.ts +0 -44
  56. package/src/index.ts +0 -164
  57. package/src/localAI/index.ts +0 -88
  58. package/src/localAI/prompts.ts +0 -50
  59. package/src/localAI/providers/base.ts +0 -120
  60. package/src/localAI/providers/ollama.ts +0 -78
  61. package/src/localAI/providers/openai.ts +0 -86
  62. package/src/localAI/types.ts +0 -67
  63. package/src/omadaClient/auth.ts +0 -178
  64. package/src/omadaClient/client.ts +0 -337
  65. package/src/omadaClient/device.ts +0 -118
  66. package/src/omadaClient/index.ts +0 -310
  67. package/src/omadaClient/network.ts +0 -284
  68. package/src/omadaClient/request.ts +0 -365
  69. package/src/omadaClient/security.ts +0 -51
  70. package/src/omadaClient/site.ts +0 -344
  71. package/src/ownerQuestions/service.ts +0 -536
  72. package/src/permissions/index.ts +0 -143
  73. package/src/resources/index.ts +0 -213
  74. package/src/server/auth.ts +0 -175
  75. package/src/server/chatFace.ts +0 -789
  76. package/src/server/chatSlice.ts +0 -57
  77. package/src/server/common.ts +0 -170
  78. package/src/server/eventSubscription.ts +0 -343
  79. package/src/server/http.ts +0 -626
  80. package/src/server/instructions.ts +0 -97
  81. package/src/server/rateLimiter.ts +0 -227
  82. package/src/server/stdio.ts +0 -41
  83. package/src/server/stream.ts +0 -205
  84. package/src/tools/ai/analyzeSensors.ts +0 -33
  85. package/src/tools/ai/index.ts +0 -20
  86. package/src/tools/common.ts +0 -442
  87. package/src/tools/homeassistant/askOwner.ts +0 -133
  88. package/src/tools/homeassistant/automationControls.ts +0 -66
  89. package/src/tools/homeassistant/calendar.ts +0 -97
  90. package/src/tools/homeassistant/callService.ts +0 -25
  91. package/src/tools/homeassistant/controlClimate.ts +0 -68
  92. package/src/tools/homeassistant/controlCover.ts +0 -48
  93. package/src/tools/homeassistant/controlFan.ts +0 -44
  94. package/src/tools/homeassistant/controlLight.ts +0 -38
  95. package/src/tools/homeassistant/controlMediaPlayer.ts +0 -50
  96. package/src/tools/homeassistant/createAutomation.ts +0 -35
  97. package/src/tools/homeassistant/deleteAutomation.ts +0 -24
  98. package/src/tools/homeassistant/entityAction.ts +0 -35
  99. package/src/tools/homeassistant/entityGuard.ts +0 -28
  100. package/src/tools/homeassistant/getAllSensors.ts +0 -46
  101. package/src/tools/homeassistant/getAutomationTrace.ts +0 -30
  102. package/src/tools/homeassistant/getDomainSummary.ts +0 -29
  103. package/src/tools/homeassistant/getEntitiesByDomain.ts +0 -56
  104. package/src/tools/homeassistant/getHistory.ts +0 -24
  105. package/src/tools/homeassistant/getState.ts +0 -24
  106. package/src/tools/homeassistant/getStates.ts +0 -46
  107. package/src/tools/homeassistant/getVersion.ts +0 -21
  108. package/src/tools/homeassistant/index.ts +0 -121
  109. package/src/tools/homeassistant/listAutomations.ts +0 -35
  110. package/src/tools/homeassistant/listEntities.ts +0 -44
  111. package/src/tools/homeassistant/listPersons.ts +0 -46
  112. package/src/tools/homeassistant/sceneAndScript.ts +0 -53
  113. package/src/tools/homeassistant/searchEntities.ts +0 -58
  114. package/src/tools/homeassistant/sendNotification.ts +0 -179
  115. package/src/tools/homeassistant/systemTools.ts +0 -52
  116. package/src/tools/homeassistant/triggerAutomation.ts +0 -24
  117. package/src/tools/index.ts +0 -78
  118. package/src/tools/infra/actions.ts +0 -95
  119. package/src/tools/infra/index.ts +0 -138
  120. package/src/tools/omada/blockClient.ts +0 -28
  121. package/src/tools/omada/cyclePoePort.ts +0 -32
  122. package/src/tools/omada/deleteClient.ts +0 -27
  123. package/src/tools/omada/disableClientRateLimit.ts +0 -23
  124. package/src/tools/omada/getClient.ts +0 -14
  125. package/src/tools/omada/getDevice.ts +0 -14
  126. package/src/tools/omada/getFirewallSetting.ts +0 -14
  127. package/src/tools/omada/getInternetInfo.ts +0 -14
  128. package/src/tools/omada/getLanNetworkList.ts +0 -14
  129. package/src/tools/omada/getLanProfileList.ts +0 -14
  130. package/src/tools/omada/getPortForwardingStatus.ts +0 -26
  131. package/src/tools/omada/getRateLimitProfiles.ts +0 -14
  132. package/src/tools/omada/getSiteNtpStatus.ts +0 -14
  133. package/src/tools/omada/getSsidDetail.ts +0 -22
  134. package/src/tools/omada/getSsidList.ts +0 -21
  135. package/src/tools/omada/getSwitchStackDetail.ts +0 -14
  136. package/src/tools/omada/getThreatList.ts +0 -42
  137. package/src/tools/omada/getWlanGroupList.ts +0 -14
  138. package/src/tools/omada/graph.ts +0 -187
  139. package/src/tools/omada/index.ts +0 -153
  140. package/src/tools/omada/listBlockedClients.ts +0 -17
  141. package/src/tools/omada/listClients.ts +0 -14
  142. package/src/tools/omada/listClientsActivity.ts +0 -25
  143. package/src/tools/omada/listClientsPastConnections.ts +0 -44
  144. package/src/tools/omada/listDevices.ts +0 -14
  145. package/src/tools/omada/listDevicesStats.ts +0 -41
  146. package/src/tools/omada/listMostActiveClients.ts +0 -14
  147. package/src/tools/omada/listSites.ts +0 -18
  148. package/src/tools/omada/namespace.ts +0 -1164
  149. package/src/tools/omada/searchDevices.ts +0 -20
  150. package/src/tools/omada/setClientRateLimit.ts +0 -26
  151. package/src/tools/omada/setClientRateLimitProfile.ts +0 -25
  152. package/src/tools/omada/setSiteNtpServers.ts +0 -36
  153. package/src/tools/omada/setSsidEnabled.ts +0 -38
  154. package/src/tools/omada/unblockClient.ts +0 -26
  155. package/src/tools/types.ts +0 -11
  156. package/src/tools/utils/pagination-schema.ts +0 -27
  157. package/src/types/ai/index.ts +0 -11
  158. package/src/types/ai/types.ts +0 -67
  159. package/src/types/homeassistant/automations.ts +0 -43
  160. package/src/types/homeassistant/calendars.ts +0 -26
  161. package/src/types/homeassistant/config.ts +0 -14
  162. package/src/types/homeassistant/entities.ts +0 -14
  163. package/src/types/homeassistant/errors.ts +0 -33
  164. package/src/types/homeassistant/haConfig.ts +0 -7
  165. package/src/types/homeassistant/history.ts +0 -13
  166. package/src/types/homeassistant/index.ts +0 -46
  167. package/src/types/homeassistant/request.ts +0 -6
  168. package/src/types/homeassistant/serviceCall.ts +0 -18
  169. package/src/types/homeassistant/states.ts +0 -58
  170. package/src/types/homeassistant/updates.ts +0 -28
  171. package/src/types/index.ts +0 -12
  172. package/src/types/omada/activeClientInfo.ts +0 -17
  173. package/src/types/omada/clientActivity.ts +0 -32
  174. package/src/types/omada/clientBlockStatus.ts +0 -11
  175. package/src/types/omada/clientPastConnection.ts +0 -82
  176. package/src/types/omada/index.ts +0 -14
  177. package/src/types/omada/omadaApiResponse.ts +0 -5
  178. package/src/types/omada/omadaClientInfo.ts +0 -11
  179. package/src/types/omada/omadaDeviceInfo.ts +0 -11
  180. package/src/types/omada/omadaDeviceStats.ts +0 -31
  181. package/src/types/omada/omadaSiteSummary.ts +0 -5
  182. package/src/types/omada/oswStackDetail.ts +0 -11
  183. package/src/types/omada/paginatedResult.ts +0 -6
  184. package/src/types/omada/rateLimitProfile.ts +0 -58
  185. package/src/types/omada/threatInfo.ts +0 -38
  186. package/src/types/omada/tokenResult.ts +0 -6
  187. package/src/types.ts +0 -2
  188. package/src/utils/config-validations.ts +0 -68
  189. package/src/utils/jwt.ts +0 -112
  190. package/src/utils/logger.ts +0 -121
  191. package/src/utils/plainText.ts +0 -48
  192. package/src/utils/redact.ts +0 -59
  193. package/src/utils/sanitizeError.ts +0 -18
  194. package/src/utils/tlsDispatcher.ts +0 -69
  195. package/src/version.ts +0 -5
  196. package/tests/bdd/automation-creation.spec.ts +0 -214
  197. package/tests/bdd/automation-management.spec.ts +0 -267
  198. package/tests/bdd/calendar.spec.ts +0 -190
  199. package/tests/bdd/configuration.spec.ts +0 -283
  200. package/tests/bdd/device-control.spec.ts +0 -344
  201. package/tests/bdd/entity-listing.spec.ts +0 -208
  202. package/tests/bdd/entity-search.spec.ts +0 -183
  203. package/tests/bdd/history-retrieval.spec.ts +0 -195
  204. package/tests/bdd/notifications.spec.ts +0 -238
  205. package/tests/bdd/service-calls.spec.ts +0 -144
  206. package/tests/bdd/state-retrieval.spec.ts +0 -204
  207. package/tests/bench/hotpaths.bench.ts +0 -68
  208. package/tests/config.test.ts +0 -355
  209. package/tests/features/automation-creation.feature +0 -36
  210. package/tests/features/automation-management.feature +0 -54
  211. package/tests/features/calendar.feature +0 -37
  212. package/tests/features/configuration.feature +0 -70
  213. package/tests/features/device-control.feature +0 -100
  214. package/tests/features/entity-listing.feature +0 -51
  215. package/tests/features/entity-search.feature +0 -44
  216. package/tests/features/history-retrieval.feature +0 -38
  217. package/tests/features/notifications.feature +0 -37
  218. package/tests/features/service-calls.feature +0 -35
  219. package/tests/features/state-retrieval.feature +0 -37
  220. package/tests/fixtures/openapi-default-slice.json +0 -290
  221. package/tests/haClient/automationTrace.test.ts +0 -59
  222. package/tests/haClient/automations.test.ts +0 -149
  223. package/tests/haClient/config.test.ts +0 -117
  224. package/tests/haClient/entities.test.ts +0 -188
  225. package/tests/haClient/events.test.ts +0 -455
  226. package/tests/haClient/eventsTls.test.ts +0 -52
  227. package/tests/haClient/history.test.ts +0 -228
  228. package/tests/haClient/index.test.ts +0 -243
  229. package/tests/haClient/request.test.ts +0 -217
  230. package/tests/haClient/services.test.ts +0 -120
  231. package/tests/haClient/states.test.ts +0 -119
  232. package/tests/haClient/updates.test.ts +0 -193
  233. package/tests/index.test.ts +0 -160
  234. package/tests/integration/homeassistant.test.ts +0 -144
  235. package/tests/integration/omada.test.ts +0 -167
  236. package/tests/integration/server.test.ts +0 -151
  237. package/tests/localAI/index.test.ts +0 -177
  238. package/tests/localAI/ollama.test.ts +0 -187
  239. package/tests/localAI/openai.test.ts +0 -257
  240. package/tests/omadaClient/actions.test.ts +0 -161
  241. package/tests/omadaClient/clientBlocks.test.ts +0 -170
  242. package/tests/omadaClient/ntp.test.ts +0 -381
  243. package/tests/omadaClient/site.test.ts +0 -37
  244. package/tests/ownerQuestions/service.test.ts +0 -499
  245. package/tests/ownerQuestions/tools.test.ts +0 -186
  246. package/tests/permissions/index.test.ts +0 -217
  247. package/tests/resources/index.test.ts +0 -288
  248. package/tests/scripts/generateJwt.test.ts +0 -44
  249. package/tests/security/accessControl.test.ts +0 -223
  250. package/tests/security/authentication.test.ts +0 -117
  251. package/tests/security/configuration.test.ts +0 -170
  252. package/tests/security/cryptoTransport.test.ts +0 -172
  253. package/tests/security/dependencies.test.ts +0 -152
  254. package/tests/security/injection.test.ts +0 -117
  255. package/tests/security/insecureDesign.test.ts +0 -121
  256. package/tests/security/logging.test.ts +0 -131
  257. package/tests/security/resourceConsumption.test.ts +0 -142
  258. package/tests/server/auth.test.ts +0 -249
  259. package/tests/server/chatFace.test.ts +0 -320
  260. package/tests/server/common.test.ts +0 -84
  261. package/tests/server/http.test.ts +0 -561
  262. package/tests/server/instructions.test.ts +0 -152
  263. package/tests/server/memoizeJsonSchema.test.ts +0 -71
  264. package/tests/server/stdio.test.ts +0 -99
  265. package/tests/server/stream.test.ts +0 -285
  266. package/tests/server/toolVisibility.http.test.ts +0 -111
  267. package/tests/server/toolVisibility.test.ts +0 -129
  268. package/tests/tools/ai/analyzeSensors.test.ts +0 -227
  269. package/tests/tools/common.test.ts +0 -123
  270. package/tests/tools/homeassistant/handlers.test.ts +0 -872
  271. package/tests/tools/infra/invokeAction.test.ts +0 -280
  272. package/tests/tools/omada/graph.test.ts +0 -385
  273. package/tests/tools/omada/handlers.test.ts +0 -897
  274. package/tests/tools/omada/namespace.test.ts +0 -483
  275. package/tests/tools/registry.test.ts +0 -168
  276. package/tests/utils/config-validations.test.ts +0 -352
  277. package/tests/utils/jwt.test.ts +0 -115
  278. package/tests/utils/logger.test.ts +0 -327
  279. package/tests/utils/redact.test.ts +0 -105
  280. package/tsconfig.json +0 -24
  281. 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
- }