@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,144 +0,0 @@
1
- /**
2
- * Integration tests for Home Assistant
3
- * These tests run against a real Home Assistant instance
4
- * Requires HA_URL and HA_TOKEN environment variables
5
- */
6
-
7
- import { describe, it, expect, beforeAll } from 'vitest';
8
- import { HaClient } from '../../src/haClient/index.js';
9
-
10
- describe('Home Assistant Integration', () => {
11
- let client: HaClient;
12
-
13
- beforeAll(() => {
14
- const baseUrl = process.env.HA_URL;
15
- const token = process.env.HA_TOKEN;
16
-
17
- if (!baseUrl || !token) {
18
- throw new Error('HA_URL and HA_TOKEN environment variables are required for integration tests');
19
- }
20
-
21
- client = new HaClient({
22
- baseUrl,
23
- token,
24
- strictSsl: process.env.HA_STRICT_SSL !== 'false',
25
- timeout: parseInt(process.env.HA_TIMEOUT || '30000'),
26
- });
27
- });
28
-
29
- describe('API Connection', () => {
30
- it('should connect to Home Assistant API', async () => {
31
- const result = await client.checkApi();
32
- expect(result.message).toBe('API running.');
33
- });
34
-
35
- it('should get Home Assistant version', async () => {
36
- const version = await client.getVersion();
37
- expect(version).toHaveProperty('version');
38
- expect(version.version).toMatch(/^\d+\.\d+\.\d+/);
39
- });
40
- });
41
-
42
- describe('Entity States', () => {
43
- it('should get all states', async () => {
44
- const states = await client.getStates();
45
- expect(Array.isArray(states)).toBe(true);
46
- expect(states.length).toBeGreaterThan(0);
47
- });
48
-
49
- it('should get state of a specific entity', async () => {
50
- // First get all states to find a valid entity_id
51
- const states = await client.getStates();
52
- expect(states.length).toBeGreaterThan(0);
53
-
54
- const entity = await client.getState(states[0].entity_id);
55
- expect(entity).not.toBeNull();
56
- expect(entity).toHaveProperty('entity_id');
57
- expect(entity).toHaveProperty('state');
58
- expect(entity).toHaveProperty('attributes');
59
- });
60
- });
61
-
62
- describe('Entity Search', () => {
63
- it('should search entities by query', async () => {
64
- const results = await client.searchEntities('sensor');
65
- expect(Array.isArray(results)).toBe(true);
66
- });
67
- });
68
-
69
- describe('Domain Operations', () => {
70
- it('should get entities by domain', async () => {
71
- const entities = await client.getEntitiesByDomain('sensor');
72
- expect(Array.isArray(entities)).toBe(true);
73
- entities.forEach((entity) => {
74
- expect(entity.entity_id).toMatch(/^sensor\./);
75
- });
76
- });
77
-
78
- it('should get domain summary', async () => {
79
- const result = await client.getDomainSummary('sensor');
80
- expect(result).toHaveProperty('domain');
81
- expect(result).toHaveProperty('count');
82
- expect(result).toHaveProperty('states');
83
- expect(result.domain).toBe('sensor');
84
- expect(result.count).toBeGreaterThan(0);
85
- });
86
- });
87
-
88
- describe('Sensors', () => {
89
- it('should get all sensors', async () => {
90
- const sensors = await client.getAllSensors();
91
- expect(Array.isArray(sensors)).toBe(true);
92
- sensors.forEach((entity) => {
93
- expect(entity.entity_id).toMatch(/^(sensor|binary_sensor)\./);
94
- });
95
- });
96
- });
97
-
98
- describe('Persons', () => {
99
- it('should get person entities via domain lookup', async () => {
100
- // The HaClient doesn't have a getPersons method directly
101
- // Persons are accessed via getEntitiesByDomain('person')
102
- const persons = await client.getEntitiesByDomain('person');
103
- expect(Array.isArray(persons)).toBe(true);
104
- persons.forEach((person) => {
105
- expect(person.entity_id).toMatch(/^person\./);
106
- });
107
- });
108
- });
109
-
110
- describe('Automations', () => {
111
- it('should list automations', async () => {
112
- const result = await client.getAutomations();
113
- expect(Array.isArray(result)).toBe(true);
114
- if (result.length > 0) {
115
- // Automation type uses 'id' not 'entity_id'
116
- expect(result[0]).toHaveProperty('id');
117
- expect(result[0]).toHaveProperty('state');
118
- expect(result[0]).toHaveProperty('alias');
119
- }
120
- });
121
- });
122
-
123
- describe('Calendars', () => {
124
- it('should list calendars (or report the integration absent)', async () => {
125
- // HA only serves /api/calendars when a calendar integration is loaded;
126
- // a bare instance 404s. Both are valid live-system states — the test
127
- // asserts the client surfaces them correctly, not that the user has
128
- // calendars configured.
129
- try {
130
- const result = await client.getCalendars();
131
- expect(Array.isArray(result)).toBe(true);
132
- } catch (error) {
133
- expect((error as { statusCode?: number; message: string }).message).toMatch(/Not Found/i);
134
- }
135
- });
136
- });
137
-
138
- describe('System', () => {
139
- it('should get system log', async () => {
140
- const result = await client.getSystemLog({ hours: 1, limit: 10 });
141
- expect(Array.isArray(result)).toBe(true);
142
- });
143
- });
144
- });
@@ -1,167 +0,0 @@
1
- /**
2
- * Integration tests for TP-Link Omada Controller
3
- * These tests run against a real Omada controller
4
- * Requires OMADA_* environment variables
5
- */
6
-
7
- import { describe, it, expect, beforeAll } from 'vitest';
8
- import { OmadaClient } from '../../src/omadaClient/index.js';
9
-
10
- describe('Omada Integration', () => {
11
- let client: OmadaClient;
12
- let siteId: string;
13
-
14
- beforeAll(async () => {
15
- const baseUrl = process.env.OMADA_BASE_URL;
16
- const clientId = process.env.OMADA_CLIENT_ID;
17
- const clientSecret = process.env.OMADA_CLIENT_SECRET;
18
- const omadacId = process.env.OMADA_OMADAC_ID;
19
-
20
- if (!baseUrl || !clientId || !clientSecret || !omadacId) {
21
- throw new Error('OMADA_BASE_URL, OMADA_CLIENT_ID, OMADA_CLIENT_SECRET, and OMADA_OMADAC_ID are required');
22
- }
23
-
24
- client = new OmadaClient({
25
- baseUrl,
26
- clientId,
27
- clientSecret,
28
- omadacId,
29
- siteId: process.env.OMADA_SITE_ID,
30
- strictSsl: process.env.OMADA_STRICT_SSL !== 'false',
31
- timeout: parseInt(process.env.OMADA_TIMEOUT || '30000'),
32
- });
33
-
34
- // Get siteId for tests
35
- siteId = process.env.OMADA_SITE_ID || '';
36
- });
37
-
38
- describe('Sites', () => {
39
- it('should list all sites', async () => {
40
- const sites = await client.listSites();
41
- expect(Array.isArray(sites)).toBe(true);
42
- expect(sites.length).toBeGreaterThan(0);
43
- expect(sites[0]).toHaveProperty('siteId');
44
- expect(sites[0]).toHaveProperty('name');
45
-
46
- // Use first site if not configured
47
- if (!siteId) {
48
- siteId = sites[0].siteId;
49
- }
50
- });
51
- });
52
-
53
- describe('Devices', () => {
54
- it('should list network devices', async () => {
55
- const devices = await client.listDevices(siteId);
56
- expect(Array.isArray(devices)).toBe(true);
57
- if (devices.length > 0) {
58
- expect(devices[0]).toHaveProperty('mac');
59
- expect(devices[0]).toHaveProperty('name');
60
- expect(devices[0]).toHaveProperty('type');
61
- expect(devices[0]).toHaveProperty('ip');
62
- }
63
- });
64
-
65
- it('should search devices globally', async () => {
66
- // searchDevices returns an array of OmadaDeviceInfo
67
- // Note: If no devices match, the API may return an empty object {} instead of []
68
- // so we need to handle both cases
69
- const results = await client.searchDevices('gateway');
70
- // Results could be an array or an empty object if no matches
71
- const isArrayOrEmpty = Array.isArray(results) || (typeof results === 'object' && results !== null);
72
- expect(isArrayOrEmpty).toBe(true);
73
- if (Array.isArray(results) && results.length > 0) {
74
- expect(results[0]).toHaveProperty('mac');
75
- }
76
- });
77
- });
78
-
79
- describe('Clients', () => {
80
- it('should list connected clients', async () => {
81
- const clients = await client.listClients(siteId);
82
- expect(Array.isArray(clients)).toBe(true);
83
- if (clients.length > 0) {
84
- expect(clients[0]).toHaveProperty('mac');
85
- expect(clients[0]).toHaveProperty('ip');
86
- }
87
- });
88
-
89
- it('should list most active clients', async () => {
90
- const clients = await client.listMostActiveClients(siteId);
91
- expect(Array.isArray(clients)).toBe(true);
92
- if (clients.length > 0) {
93
- expect(clients[0]).toHaveProperty('name');
94
- // Controller v5 exposed `totalTraffic`; OC200 v6 renamed the traffic
95
- // field. Assert a traffic-ish field exists rather than pinning the
96
- // v5 name (discovered live on OC200 6.2, 2026-07-11).
97
- const keys = Object.keys(clients[0] as Record<string, unknown>);
98
- expect(keys.some((k) => /traffic|activity/i.test(k))).toBe(true);
99
- }
100
- });
101
- });
102
-
103
- describe('Network Configuration', () => {
104
- it('should get internet info', async () => {
105
- const info = await client.getInternetInfo(siteId);
106
- expect(info).toHaveProperty('wanPortSettings');
107
- });
108
-
109
- it('should get LAN network list', async () => {
110
- const networks = await client.getLanNetworkList(siteId);
111
- expect(Array.isArray(networks)).toBe(true);
112
- if (networks.length > 0) {
113
- expect(networks[0]).toHaveProperty('name');
114
- expect(networks[0]).toHaveProperty('vlan');
115
- }
116
- });
117
-
118
- it('should get WLAN group list', async () => {
119
- const groups = await client.getWlanGroupList(siteId);
120
- expect(Array.isArray(groups)).toBe(true);
121
- });
122
- });
123
-
124
- describe('Rate Limiting', () => {
125
- it('should get rate limit profiles', async () => {
126
- const profiles = await client.getRateLimitProfiles(siteId);
127
- expect(Array.isArray(profiles)).toBe(true);
128
- if (profiles.length > 0) {
129
- expect(profiles[0]).toHaveProperty('profileId');
130
- expect(profiles[0]).toHaveProperty('name');
131
- }
132
- });
133
- });
134
-
135
- describe('Security', () => {
136
- it('should get threat list (may be empty)', async () => {
137
- // The threat list API may return 400 if IDS/IPS is not enabled or configured
138
- // This test catches the error and passes if no threats are available.
139
- // NB: archived/page/pageSize/startTime/endTime are REQUIRED — controller
140
- // v5 tolerated their absence, OC200 v6 rejects nulls ("archived should
141
- // not be null", live finding 2026-07-11). The MCP tool schema already
142
- // requires them; this test used to cheat with `{}`.
143
- try {
144
- const now = Math.floor(Date.now() / 1000);
145
- const result = await client.getThreatList({
146
- archived: false,
147
- page: 1,
148
- pageSize: 10,
149
- startTime: now - 7 * 24 * 3600,
150
- endTime: now,
151
- });
152
- // If we get here, the API worked - check structure
153
- expect(result).toHaveProperty('data');
154
- expect(Array.isArray(result.data)).toBe(true);
155
- } catch (error) {
156
- // Some Omada setups may not have threat detection enabled
157
- // This is acceptable - skip rather than fail
158
- if (error instanceof Error && error.message.includes('400')) {
159
- // API returned 400 - threat detection may not be enabled
160
- expect(true).toBe(true);
161
- } else {
162
- throw error;
163
- }
164
- }
165
- });
166
- });
167
- });
@@ -1,151 +0,0 @@
1
- /**
2
- * Integration tests for MCP Server
3
- * Tests server initialization with real clients
4
- */
5
-
6
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
7
- import { HaClient } from '../../src/haClient/index.js';
8
- import { OmadaClient } from '../../src/omadaClient/index.js';
9
- import { createServer } from '../../src/server/common.js';
10
- import { generateInstructions } from '../../src/server/instructions.js';
11
-
12
- describe('Server Integration', () => {
13
- let haClient: HaClient | undefined;
14
- let omadaClient: OmadaClient | undefined;
15
-
16
- beforeAll(async () => {
17
- // Initialize HA client if configured
18
- if (process.env.HA_URL && process.env.HA_TOKEN) {
19
- haClient = new HaClient({
20
- baseUrl: process.env.HA_URL,
21
- token: process.env.HA_TOKEN,
22
- strictSsl: process.env.HA_STRICT_SSL !== 'false',
23
- timeout: parseInt(process.env.HA_TIMEOUT || '30000'),
24
- });
25
- }
26
-
27
- // Initialize Omada client if configured
28
- if (
29
- process.env.OMADA_BASE_URL &&
30
- process.env.OMADA_CLIENT_ID &&
31
- process.env.OMADA_CLIENT_SECRET &&
32
- process.env.OMADA_OMADAC_ID
33
- ) {
34
- omadaClient = new OmadaClient({
35
- baseUrl: process.env.OMADA_BASE_URL,
36
- clientId: process.env.OMADA_CLIENT_ID,
37
- clientSecret: process.env.OMADA_CLIENT_SECRET,
38
- omadacId: process.env.OMADA_OMADAC_ID,
39
- siteId: process.env.OMADA_SITE_ID,
40
- strictSsl: process.env.OMADA_STRICT_SSL !== 'false',
41
- timeout: parseInt(process.env.OMADA_TIMEOUT || '30000'),
42
- });
43
- }
44
- });
45
-
46
- describe('Server Creation', () => {
47
- it('should create server with HA client', () => {
48
- if (!haClient) {
49
- console.log('Skipping: HA not configured');
50
- return;
51
- }
52
-
53
- const server = createServer({ haClient });
54
- expect(server).toBeDefined();
55
- expect(server.server).toBeDefined();
56
- });
57
-
58
- it('should create server with Omada client', () => {
59
- if (!omadaClient) {
60
- console.log('Skipping: Omada not configured');
61
- return;
62
- }
63
-
64
- const server = createServer({ omadaClient });
65
- expect(server).toBeDefined();
66
- });
67
-
68
- it('should create server with all clients', () => {
69
- const server = createServer({
70
- haClient,
71
- omadaClient,
72
- });
73
- expect(server).toBeDefined();
74
- });
75
- });
76
-
77
- describe('Server Instructions', () => {
78
- it('should generate instructions for HA only', () => {
79
- const instructions = generateInstructions({
80
- haEnabled: true,
81
- omadaEnabled: false,
82
- aiEnabled: false,
83
- });
84
-
85
- expect(instructions).toContain('MCP-HA-Connect');
86
- expect(instructions).toContain('ENTITY QUERIES');
87
- expect(instructions).not.toContain('OMADA');
88
- });
89
-
90
- it('should generate instructions for Omada only', () => {
91
- const instructions = generateInstructions({
92
- haEnabled: false,
93
- omadaEnabled: true,
94
- aiEnabled: false,
95
- });
96
-
97
- expect(instructions).toContain('MCP-HA-Connect');
98
- expect(instructions).toContain('OMADA');
99
- expect(instructions).not.toContain('ENTITY QUERIES');
100
- });
101
-
102
- it('should generate instructions for all plugins', () => {
103
- const instructions = generateInstructions({
104
- haEnabled: true,
105
- omadaEnabled: true,
106
- aiEnabled: true,
107
- });
108
-
109
- expect(instructions).toContain('MCP-HA-Connect');
110
- expect(instructions).toContain('ENTITY QUERIES');
111
- expect(instructions).toContain('OMADA');
112
- expect(instructions).toContain('AI ANALYSIS');
113
- expect(instructions).toContain('INTEGRATION');
114
- });
115
-
116
- it('should generate concise instructions under the size ceiling', () => {
117
- const instructions = generateInstructions({
118
- haEnabled: true,
119
- omadaEnabled: true,
120
- aiEnabled: true,
121
- });
122
-
123
- // Keep in sync with tests/server/instructions.test.ts — the ceiling was
124
- // raised 2000 -> 2200 in v1.6.0 (ACTIONS line); this integration twin was
125
- // stale until the suite was actually run (2026-07-11).
126
- expect(instructions.length).toBeLessThan(2200);
127
- });
128
- });
129
-
130
- describe('Client Connectivity', () => {
131
- it('should verify HA connection', async () => {
132
- if (!haClient) {
133
- console.log('Skipping: HA not configured');
134
- return;
135
- }
136
-
137
- const result = await haClient.checkApi();
138
- expect(result.message).toBe('API running.');
139
- });
140
-
141
- it('should verify Omada connection', async () => {
142
- if (!omadaClient) {
143
- console.log('Skipping: Omada not configured');
144
- return;
145
- }
146
-
147
- const sites = await omadaClient.listSites();
148
- expect(sites.length).toBeGreaterThan(0);
149
- });
150
- });
151
- });
@@ -1,177 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import type { EnvironmentConfig } from '../../src/config.js';
3
- import type { AIProviderType } from '../../src/localAI/types.js';
4
-
5
- // Create mock functions that will be used by the mock implementations
6
- const mockOllamaAnalyze = vi.fn();
7
- const mockOllamaHealth = vi.fn();
8
- const mockOpenAIAnalyze = vi.fn();
9
- const mockOpenAIHealth = vi.fn();
10
-
11
- // Mock the providers with class-style constructors
12
- vi.mock('../../src/localAI/providers/ollama.js', () => {
13
- return {
14
- OllamaProvider: class MockOllamaProvider {
15
- analyzeSensors = mockOllamaAnalyze;
16
- checkHealth = mockOllamaHealth;
17
- getProviderName() {
18
- return 'ollama';
19
- }
20
- },
21
- };
22
- });
23
-
24
- vi.mock('../../src/localAI/providers/openai.js', () => {
25
- return {
26
- OpenAICompatibleProvider: class MockOpenAIProvider {
27
- analyzeSensors = mockOpenAIAnalyze;
28
- checkHealth = mockOpenAIHealth;
29
- getProviderName() {
30
- return 'openai-compatible';
31
- }
32
- },
33
- };
34
- });
35
-
36
- import { LocalAIClient } from '../../src/localAI/index.js';
37
-
38
- describe('LocalAIClient', () => {
39
- const createConfig = (provider: AIProviderType = 'ollama'): EnvironmentConfig =>
40
- ({
41
- aiProvider: provider,
42
- aiUrl: 'http://ollama.10.0.0.17.nip.io:11434',
43
- aiModel: 'qwen3:14b',
44
- aiTimeout: 60000,
45
- aiApiKey: undefined,
46
- // Other required config fields
47
- baseUrl: 'http://homeassistant.10.0.0.19.nip.io:8123',
48
- token: 'test-token',
49
- strictSsl: true,
50
- timeout: 30000,
51
- logLevel: 'info',
52
- logFormat: 'plain',
53
- useHttp: false,
54
- stateful: false,
55
-
56
- httpEnableHealthcheck: true,
57
- httpAllowCors: true,
58
- }) as EnvironmentConfig;
59
-
60
- beforeEach(() => {
61
- vi.clearAllMocks();
62
- });
63
-
64
- describe('constructor', () => {
65
- it('should create OllamaProvider for ollama provider type', () => {
66
- const config = createConfig('ollama');
67
- const client = new LocalAIClient(config);
68
-
69
- expect(client.getProviderName()).toBe('ollama');
70
- });
71
-
72
- it('should create OpenAICompatibleProvider for openai provider type', () => {
73
- const config = createConfig('openai');
74
- const client = new LocalAIClient(config);
75
-
76
- expect(client.getProviderName()).toBe('openai-compatible');
77
- });
78
-
79
- it('should default to OllamaProvider when provider is unknown', () => {
80
- const config = createConfig('ollama');
81
- // Force unknown provider
82
- (config as unknown as { aiProvider: string }).aiProvider = 'unknown';
83
- const client = new LocalAIClient(config);
84
-
85
- // Should fall back to ollama
86
- expect(client.getProviderName()).toBe('ollama');
87
- });
88
- });
89
-
90
- describe('analyzeSensors', () => {
91
- it('should delegate to the provider', async () => {
92
- const mockResult = {
93
- severity: 'critical' as const,
94
- issue: 'Test issue',
95
- reasoning: 'Test reasoning',
96
- recommended_actions: [],
97
- ssh_diagnostics: [],
98
- confidence: 0.9,
99
- model: 'qwen3:14b',
100
- provider: 'ollama',
101
- response_time: 1.5,
102
- };
103
- mockOllamaAnalyze.mockResolvedValueOnce(mockResult);
104
-
105
- const config = createConfig('ollama');
106
- const client = new LocalAIClient(config);
107
- const sensors = { 'sensor.test': { state: '25' } };
108
-
109
- const result = await client.analyzeSensors(sensors);
110
-
111
- expect(mockOllamaAnalyze).toHaveBeenCalledWith(sensors);
112
- expect(result).toEqual(mockResult);
113
- });
114
- });
115
-
116
- describe('checkHealth', () => {
117
- it('should delegate to the provider', async () => {
118
- mockOllamaHealth.mockResolvedValueOnce(true);
119
-
120
- const config = createConfig('ollama');
121
- const client = new LocalAIClient(config);
122
-
123
- const result = await client.checkHealth();
124
-
125
- expect(mockOllamaHealth).toHaveBeenCalled();
126
- expect(result).toBe(true);
127
- });
128
-
129
- it('should return false when provider is unhealthy', async () => {
130
- mockOllamaHealth.mockResolvedValueOnce(false);
131
-
132
- const config = createConfig('ollama');
133
- const client = new LocalAIClient(config);
134
-
135
- const result = await client.checkHealth();
136
-
137
- expect(result).toBe(false);
138
- });
139
- });
140
-
141
- describe('getModelName', () => {
142
- it('should return the configured model name', () => {
143
- const config = createConfig('ollama');
144
- config.aiModel = 'phi4:14b';
145
- const client = new LocalAIClient(config);
146
-
147
- expect(client.getModelName()).toBe('phi4:14b');
148
- });
149
- });
150
-
151
- describe('getConfig', () => {
152
- it('should return the provider configuration', () => {
153
- const config = createConfig('ollama');
154
- config.aiApiKey = 'test-key';
155
- const client = new LocalAIClient(config);
156
-
157
- const providerConfig = client.getConfig();
158
-
159
- expect(providerConfig.provider).toBe('ollama');
160
- expect(providerConfig.baseUrl).toBe('http://ollama.10.0.0.17.nip.io:11434');
161
- expect(providerConfig.model).toBe('qwen3:14b');
162
- expect(providerConfig.timeout).toBe(60000);
163
- expect(providerConfig.apiKey).toBe('test-key');
164
- });
165
-
166
- it('should return a copy of the config (immutable)', () => {
167
- const config = createConfig('ollama');
168
- const client = new LocalAIClient(config);
169
-
170
- const providerConfig1 = client.getConfig();
171
- const providerConfig2 = client.getConfig();
172
-
173
- expect(providerConfig1).not.toBe(providerConfig2);
174
- expect(providerConfig1).toEqual(providerConfig2);
175
- });
176
- });
177
- });