unifi-mcp-worker 1.2.2 → 1.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.
package/README.md CHANGED
@@ -32,7 +32,7 @@ The Durable Object persists location registrations in SQLite and maintains live
32
32
  ### Install
33
33
 
34
34
  ```bash
35
- curl -fsSL https://raw.githubusercontent.com/sirkirby/unifi-mcp-worker/main/install.sh | bash
35
+ curl -fsSL https://raw.githubusercontent.com/sirkirby/unifi-mcp/main/apps/worker/install.sh | bash
36
36
  ```
37
37
 
38
38
  Or install the CLI directly:
@@ -119,13 +119,14 @@ curl -X POST https://your-worker.workers.dev/mcp \
119
119
 
120
120
  ### Built-in Meta-Tools
121
121
 
122
- The relay always exposes three meta-tools regardless of registration mode:
122
+ The relay always exposes these meta-tools regardless of registration mode:
123
123
 
124
124
  | Tool | Purpose |
125
125
  |------|---------|
126
126
  | `unifi_tool_index` | List all available UniFi tools with optional category or search filter |
127
127
  | `unifi_execute` | Execute any UniFi tool by name; use `__location` to target a specific site |
128
128
  | `unifi_batch` | Execute multiple UniFi tools in a single request |
129
+ | `unifi_location_timeline` | Query a unified event timeline across connected locations and products |
129
130
 
130
131
  ---
131
132
 
@@ -207,4 +208,4 @@ Read-only operations (tools with `readOnlyHint: true`) are automatically fanned
207
208
 
208
209
  ## Related
209
210
 
210
- - [unifi-mcp](https://github.com/sirkirby/unifi-mcp) — UniFi MCP server and `unifi-mcp-relay` client that connects to this relay worker
211
+ - [unifi-mcp](https://github.com/sirkirby/unifi-mcp) — source repository for this worker, the UniFi MCP servers, and the `unifi-mcp-relay` client
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unifi-mcp-worker",
3
- "version": "1.2.2",
3
+ "version": "1.3.1",
4
4
  "description": "CLI to deploy and manage the UniFi MCP Cloudflare Worker relay",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,6 +32,7 @@
32
32
  "license": "MIT",
33
33
  "repository": {
34
34
  "type": "git",
35
- "url": "https://github.com/sirkirby/unifi-mcp-worker.git"
35
+ "url": "https://github.com/sirkirby/unifi-mcp.git",
36
+ "directory": "apps/worker"
36
37
  }
37
38
  }
@@ -8,31 +8,32 @@
8
8
  "name": "unifi-mcp-worker-source",
9
9
  "version": "1.0.0",
10
10
  "devDependencies": {
11
- "@cloudflare/workers-types": "^4.20250313.0",
11
+ "@cloudflare/workers-types": "^4.20260515.1",
12
+ "@types/node": "^22.19.19",
12
13
  "typescript": "^5.7.0",
13
14
  "vitest": "^3.0.0",
14
- "wrangler": "^4.0.0"
15
+ "wrangler": "^4.92.0"
15
16
  }
16
17
  },
17
18
  "node_modules/@cloudflare/kv-asset-handler": {
18
- "version": "0.4.2",
19
- "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz",
20
- "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==",
19
+ "version": "0.5.0",
20
+ "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz",
21
+ "integrity": "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==",
21
22
  "dev": true,
22
23
  "license": "MIT OR Apache-2.0",
23
24
  "engines": {
24
- "node": ">=18.0.0"
25
+ "node": ">=22.0.0"
25
26
  }
26
27
  },
27
28
  "node_modules/@cloudflare/unenv-preset": {
28
- "version": "2.16.0",
29
- "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.0.tgz",
30
- "integrity": "sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==",
29
+ "version": "2.16.1",
30
+ "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.1.tgz",
31
+ "integrity": "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==",
31
32
  "dev": true,
32
33
  "license": "MIT OR Apache-2.0",
33
34
  "peerDependencies": {
34
35
  "unenv": "2.0.0-rc.24",
35
- "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0"
36
+ "workerd": ">1.20260305.0 <2.0.0-0"
36
37
  },
37
38
  "peerDependenciesMeta": {
38
39
  "workerd": {
@@ -41,9 +42,9 @@
41
42
  }
42
43
  },
43
44
  "node_modules/@cloudflare/workerd-darwin-64": {
44
- "version": "1.20260317.1",
45
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260317.1.tgz",
46
- "integrity": "sha512-8hjh3sPMwY8M/zedq3/sXoA2Q4BedlGufn3KOOleIG+5a4ReQKLlUah140D7J6zlKmYZAFMJ4tWC7hCuI/s79g==",
45
+ "version": "1.20260515.1",
46
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260515.1.tgz",
47
+ "integrity": "sha512-Wtw44el2pNbzixvTkWdfeBDTrQwQbJRz7/JUvPKV27I0pQWXbhNJPpM8cstq/pbrU5AGcA/HjFH6yPMRTIRKig==",
47
48
  "cpu": [
48
49
  "x64"
49
50
  ],
@@ -58,9 +59,9 @@
58
59
  }
59
60
  },
60
61
  "node_modules/@cloudflare/workerd-darwin-arm64": {
61
- "version": "1.20260317.1",
62
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260317.1.tgz",
63
- "integrity": "sha512-M/MnNyvO5HMgoIdr3QHjdCj2T1ki9gt0vIUnxYxBu9ISXS/jgtMl6chUVPJ7zHYBn9MyYr8ByeN6frjYxj0MGg==",
62
+ "version": "1.20260515.1",
63
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260515.1.tgz",
64
+ "integrity": "sha512-X8EqkZej6FfmhF9AVAQ3FhyQRr9acS4RcDunMU2YiuxKHF1IU8zzH3vY30/POaG+rUu9vGDp/VgUl49VPenHJQ==",
64
65
  "cpu": [
65
66
  "arm64"
66
67
  ],
@@ -75,9 +76,9 @@
75
76
  }
76
77
  },
77
78
  "node_modules/@cloudflare/workerd-linux-64": {
78
- "version": "1.20260317.1",
79
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260317.1.tgz",
80
- "integrity": "sha512-1ltuEjkRcS3fsVF7CxsKlWiRmzq2ZqMfqDN0qUOgbUwkpXsLVJsXmoblaLf5OP00ELlcgF0QsN0p2xPEua4Uug==",
79
+ "version": "1.20260515.1",
80
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260515.1.tgz",
81
+ "integrity": "sha512-CDC89QxQ7Y7t7RG1Jd9vj/qolE1sQRkI2OSEuV5BMJi0vW/gV4OVG6xjpdK3b1OYnSWDzF7NpvlR5Yg86q7k4g==",
81
82
  "cpu": [
82
83
  "x64"
83
84
  ],
@@ -92,9 +93,9 @@
92
93
  }
93
94
  },
94
95
  "node_modules/@cloudflare/workerd-linux-arm64": {
95
- "version": "1.20260317.1",
96
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260317.1.tgz",
97
- "integrity": "sha512-3QrNnPF1xlaNwkHpasvRvAMidOvQs2NhXQmALJrEfpIJ/IDL2la8g499yXp3eqhG3hVMCB07XVY149GTs42Xtw==",
96
+ "version": "1.20260515.1",
97
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260515.1.tgz",
98
+ "integrity": "sha512-WxbW/PToYES4fvHXzsr/5qOiETQs/Z9iZ0mjSZAiEwq5cMLZemzGN0COx+uFb9OvQwzh6Pg159qPFnw3+i9FuA==",
98
99
  "cpu": [
99
100
  "arm64"
100
101
  ],
@@ -109,9 +110,9 @@
109
110
  }
110
111
  },
111
112
  "node_modules/@cloudflare/workerd-windows-64": {
112
- "version": "1.20260317.1",
113
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260317.1.tgz",
114
- "integrity": "sha512-MfZTz+7LfuIpMGTa3RLXHX8Z/pnycZLItn94WRdHr8LPVet+C5/1Nzei399w/jr3+kzT4pDKk26JF/tlI5elpQ==",
113
+ "version": "1.20260515.1",
114
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260515.1.tgz",
115
+ "integrity": "sha512-WmV/iv+MHjYsvkcMVzpM2B5/mf06UUkdpVhZrtMfV9graWjBGPYFvE/eab8748RPVGKh1Xe1vXofLzDSwc08lA==",
115
116
  "cpu": [
116
117
  "x64"
117
118
  ],
@@ -126,9 +127,9 @@
126
127
  }
127
128
  },
128
129
  "node_modules/@cloudflare/workers-types": {
129
- "version": "4.20260317.1",
130
- "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260317.1.tgz",
131
- "integrity": "sha512-+G4eVwyCpm8Au1ex8vQBCuA9wnwqetz4tPNRoB/53qvktERWBRMQnrtvC1k584yRE3emMThtuY0gWshvSJ++PQ==",
130
+ "version": "4.20260515.1",
131
+ "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260515.1.tgz",
132
+ "integrity": "sha512-ruz+9tcG2iIKq5mXU2F00lL6KF5d4QfbAk7fNgvvqf5BelxDXaieyZmSnvA4sQBuemUstt4AEDN9CmJKmfCsDw==",
132
133
  "dev": true,
133
134
  "license": "MIT OR Apache-2.0"
134
135
  },
@@ -146,9 +147,9 @@
146
147
  }
147
148
  },
148
149
  "node_modules/@emnapi/runtime": {
149
- "version": "1.9.1",
150
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz",
151
- "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==",
150
+ "version": "1.10.0",
151
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
152
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
152
153
  "dev": true,
153
154
  "license": "MIT",
154
155
  "optional": true,
@@ -696,9 +697,6 @@
696
697
  "arm"
697
698
  ],
698
699
  "dev": true,
699
- "libc": [
700
- "glibc"
701
- ],
702
700
  "license": "LGPL-3.0-or-later",
703
701
  "optional": true,
704
702
  "os": [
@@ -716,9 +714,6 @@
716
714
  "arm64"
717
715
  ],
718
716
  "dev": true,
719
- "libc": [
720
- "glibc"
721
- ],
722
717
  "license": "LGPL-3.0-or-later",
723
718
  "optional": true,
724
719
  "os": [
@@ -736,9 +731,6 @@
736
731
  "ppc64"
737
732
  ],
738
733
  "dev": true,
739
- "libc": [
740
- "glibc"
741
- ],
742
734
  "license": "LGPL-3.0-or-later",
743
735
  "optional": true,
744
736
  "os": [
@@ -756,9 +748,6 @@
756
748
  "riscv64"
757
749
  ],
758
750
  "dev": true,
759
- "libc": [
760
- "glibc"
761
- ],
762
751
  "license": "LGPL-3.0-or-later",
763
752
  "optional": true,
764
753
  "os": [
@@ -776,9 +765,6 @@
776
765
  "s390x"
777
766
  ],
778
767
  "dev": true,
779
- "libc": [
780
- "glibc"
781
- ],
782
768
  "license": "LGPL-3.0-or-later",
783
769
  "optional": true,
784
770
  "os": [
@@ -796,9 +782,6 @@
796
782
  "x64"
797
783
  ],
798
784
  "dev": true,
799
- "libc": [
800
- "glibc"
801
- ],
802
785
  "license": "LGPL-3.0-or-later",
803
786
  "optional": true,
804
787
  "os": [
@@ -816,9 +799,6 @@
816
799
  "arm64"
817
800
  ],
818
801
  "dev": true,
819
- "libc": [
820
- "musl"
821
- ],
822
802
  "license": "LGPL-3.0-or-later",
823
803
  "optional": true,
824
804
  "os": [
@@ -836,9 +816,6 @@
836
816
  "x64"
837
817
  ],
838
818
  "dev": true,
839
- "libc": [
840
- "musl"
841
- ],
842
819
  "license": "LGPL-3.0-or-later",
843
820
  "optional": true,
844
821
  "os": [
@@ -856,9 +833,6 @@
856
833
  "arm"
857
834
  ],
858
835
  "dev": true,
859
- "libc": [
860
- "glibc"
861
- ],
862
836
  "license": "Apache-2.0",
863
837
  "optional": true,
864
838
  "os": [
@@ -882,9 +856,6 @@
882
856
  "arm64"
883
857
  ],
884
858
  "dev": true,
885
- "libc": [
886
- "glibc"
887
- ],
888
859
  "license": "Apache-2.0",
889
860
  "optional": true,
890
861
  "os": [
@@ -908,9 +879,6 @@
908
879
  "ppc64"
909
880
  ],
910
881
  "dev": true,
911
- "libc": [
912
- "glibc"
913
- ],
914
882
  "license": "Apache-2.0",
915
883
  "optional": true,
916
884
  "os": [
@@ -934,9 +902,6 @@
934
902
  "riscv64"
935
903
  ],
936
904
  "dev": true,
937
- "libc": [
938
- "glibc"
939
- ],
940
905
  "license": "Apache-2.0",
941
906
  "optional": true,
942
907
  "os": [
@@ -960,9 +925,6 @@
960
925
  "s390x"
961
926
  ],
962
927
  "dev": true,
963
- "libc": [
964
- "glibc"
965
- ],
966
928
  "license": "Apache-2.0",
967
929
  "optional": true,
968
930
  "os": [
@@ -986,9 +948,6 @@
986
948
  "x64"
987
949
  ],
988
950
  "dev": true,
989
- "libc": [
990
- "glibc"
991
- ],
992
951
  "license": "Apache-2.0",
993
952
  "optional": true,
994
953
  "os": [
@@ -1012,9 +971,6 @@
1012
971
  "arm64"
1013
972
  ],
1014
973
  "dev": true,
1015
- "libc": [
1016
- "musl"
1017
- ],
1018
974
  "license": "Apache-2.0",
1019
975
  "optional": true,
1020
976
  "os": [
@@ -1038,9 +994,6 @@
1038
994
  "x64"
1039
995
  ],
1040
996
  "dev": true,
1041
- "libc": [
1042
- "musl"
1043
- ],
1044
997
  "license": "Apache-2.0",
1045
998
  "optional": true,
1046
999
  "os": [
@@ -1285,9 +1238,6 @@
1285
1238
  "arm"
1286
1239
  ],
1287
1240
  "dev": true,
1288
- "libc": [
1289
- "glibc"
1290
- ],
1291
1241
  "license": "MIT",
1292
1242
  "optional": true,
1293
1243
  "os": [
@@ -1302,9 +1252,6 @@
1302
1252
  "arm"
1303
1253
  ],
1304
1254
  "dev": true,
1305
- "libc": [
1306
- "musl"
1307
- ],
1308
1255
  "license": "MIT",
1309
1256
  "optional": true,
1310
1257
  "os": [
@@ -1319,9 +1266,6 @@
1319
1266
  "arm64"
1320
1267
  ],
1321
1268
  "dev": true,
1322
- "libc": [
1323
- "glibc"
1324
- ],
1325
1269
  "license": "MIT",
1326
1270
  "optional": true,
1327
1271
  "os": [
@@ -1336,9 +1280,6 @@
1336
1280
  "arm64"
1337
1281
  ],
1338
1282
  "dev": true,
1339
- "libc": [
1340
- "musl"
1341
- ],
1342
1283
  "license": "MIT",
1343
1284
  "optional": true,
1344
1285
  "os": [
@@ -1353,9 +1294,6 @@
1353
1294
  "loong64"
1354
1295
  ],
1355
1296
  "dev": true,
1356
- "libc": [
1357
- "glibc"
1358
- ],
1359
1297
  "license": "MIT",
1360
1298
  "optional": true,
1361
1299
  "os": [
@@ -1370,9 +1308,6 @@
1370
1308
  "loong64"
1371
1309
  ],
1372
1310
  "dev": true,
1373
- "libc": [
1374
- "musl"
1375
- ],
1376
1311
  "license": "MIT",
1377
1312
  "optional": true,
1378
1313
  "os": [
@@ -1387,9 +1322,6 @@
1387
1322
  "ppc64"
1388
1323
  ],
1389
1324
  "dev": true,
1390
- "libc": [
1391
- "glibc"
1392
- ],
1393
1325
  "license": "MIT",
1394
1326
  "optional": true,
1395
1327
  "os": [
@@ -1404,9 +1336,6 @@
1404
1336
  "ppc64"
1405
1337
  ],
1406
1338
  "dev": true,
1407
- "libc": [
1408
- "musl"
1409
- ],
1410
1339
  "license": "MIT",
1411
1340
  "optional": true,
1412
1341
  "os": [
@@ -1421,9 +1350,6 @@
1421
1350
  "riscv64"
1422
1351
  ],
1423
1352
  "dev": true,
1424
- "libc": [
1425
- "glibc"
1426
- ],
1427
1353
  "license": "MIT",
1428
1354
  "optional": true,
1429
1355
  "os": [
@@ -1438,9 +1364,6 @@
1438
1364
  "riscv64"
1439
1365
  ],
1440
1366
  "dev": true,
1441
- "libc": [
1442
- "musl"
1443
- ],
1444
1367
  "license": "MIT",
1445
1368
  "optional": true,
1446
1369
  "os": [
@@ -1455,9 +1378,6 @@
1455
1378
  "s390x"
1456
1379
  ],
1457
1380
  "dev": true,
1458
- "libc": [
1459
- "glibc"
1460
- ],
1461
1381
  "license": "MIT",
1462
1382
  "optional": true,
1463
1383
  "os": [
@@ -1472,9 +1392,6 @@
1472
1392
  "x64"
1473
1393
  ],
1474
1394
  "dev": true,
1475
- "libc": [
1476
- "glibc"
1477
- ],
1478
1395
  "license": "MIT",
1479
1396
  "optional": true,
1480
1397
  "os": [
@@ -1489,9 +1406,6 @@
1489
1406
  "x64"
1490
1407
  ],
1491
1408
  "dev": true,
1492
- "libc": [
1493
- "musl"
1494
- ],
1495
1409
  "license": "MIT",
1496
1410
  "optional": true,
1497
1411
  "os": [
@@ -1627,6 +1541,16 @@
1627
1541
  "dev": true,
1628
1542
  "license": "MIT"
1629
1543
  },
1544
+ "node_modules/@types/node": {
1545
+ "version": "22.19.19",
1546
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz",
1547
+ "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==",
1548
+ "dev": true,
1549
+ "license": "MIT",
1550
+ "dependencies": {
1551
+ "undici-types": "~6.21.0"
1552
+ }
1553
+ },
1630
1554
  "node_modules/@vitest/expect": {
1631
1555
  "version": "3.2.4",
1632
1556
  "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
@@ -1995,16 +1919,16 @@
1995
1919
  }
1996
1920
  },
1997
1921
  "node_modules/miniflare": {
1998
- "version": "4.20260317.1",
1999
- "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260317.1.tgz",
2000
- "integrity": "sha512-A3csI1HXEIfqe3oscgpoRMHdYlkReQKPH/g5JE53vFSjoM6YIAOGAzyDNeYffwd9oQkPWDj9xER8+vpxei8klA==",
1922
+ "version": "4.20260515.0",
1923
+ "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260515.0.tgz",
1924
+ "integrity": "sha512-2j0oQWizk1Eu4Cm8tDX7Z+Nsjd0nebIj1TQcQ+Oy1QKeo0Ay9+bdn8wfLAtOj9znDCybDCUlnS1+nYvKXEdfNg==",
2001
1925
  "dev": true,
2002
1926
  "license": "MIT",
2003
1927
  "dependencies": {
2004
1928
  "@cspotcode/source-map-support": "0.8.1",
2005
1929
  "sharp": "^0.34.5",
2006
- "undici": "7.24.4",
2007
- "workerd": "1.20260317.1",
1930
+ "undici": "7.24.8",
1931
+ "workerd": "1.20260515.1",
2008
1932
  "ws": "8.18.0",
2009
1933
  "youch": "4.1.0-beta.10"
2010
1934
  },
@@ -2012,7 +1936,7 @@
2012
1936
  "miniflare": "bootstrap.js"
2013
1937
  },
2014
1938
  "engines": {
2015
- "node": ">=18.0.0"
1939
+ "node": ">=22.0.0"
2016
1940
  }
2017
1941
  },
2018
1942
  "node_modules/ms": {
@@ -2160,9 +2084,9 @@
2160
2084
  }
2161
2085
  },
2162
2086
  "node_modules/semver": {
2163
- "version": "7.7.4",
2164
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
2165
- "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
2087
+ "version": "7.8.0",
2088
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
2089
+ "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
2166
2090
  "dev": true,
2167
2091
  "license": "ISC",
2168
2092
  "bin": {
@@ -2358,15 +2282,22 @@
2358
2282
  }
2359
2283
  },
2360
2284
  "node_modules/undici": {
2361
- "version": "7.24.4",
2362
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz",
2363
- "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==",
2285
+ "version": "7.24.8",
2286
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.8.tgz",
2287
+ "integrity": "sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==",
2364
2288
  "dev": true,
2365
2289
  "license": "MIT",
2366
2290
  "engines": {
2367
2291
  "node": ">=20.18.1"
2368
2292
  }
2369
2293
  },
2294
+ "node_modules/undici-types": {
2295
+ "version": "6.21.0",
2296
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
2297
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
2298
+ "dev": true,
2299
+ "license": "MIT"
2300
+ },
2370
2301
  "node_modules/unenv": {
2371
2302
  "version": "2.0.0-rc.24",
2372
2303
  "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz",
@@ -2566,9 +2497,9 @@
2566
2497
  }
2567
2498
  },
2568
2499
  "node_modules/workerd": {
2569
- "version": "1.20260317.1",
2570
- "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260317.1.tgz",
2571
- "integrity": "sha512-ZuEq1OdrJBS+NV+L5HMYPCzVn49a2O60slQiiLpG44jqtlOo+S167fWC76kEXteXLLLydeuRrluRel7WdOUa4g==",
2500
+ "version": "1.20260515.1",
2501
+ "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260515.1.tgz",
2502
+ "integrity": "sha512-MjKOJLcvU45xXedQowvuiHtJTxu4WTHYQeIlF7YmjuqhiI6dImTFxWCEoRQHiskztxuVSNEmdO7/0UfDu6OMnQ==",
2572
2503
  "dev": true,
2573
2504
  "hasInstallScript": true,
2574
2505
  "license": "Apache-2.0",
@@ -2579,41 +2510,41 @@
2579
2510
  "node": ">=16"
2580
2511
  },
2581
2512
  "optionalDependencies": {
2582
- "@cloudflare/workerd-darwin-64": "1.20260317.1",
2583
- "@cloudflare/workerd-darwin-arm64": "1.20260317.1",
2584
- "@cloudflare/workerd-linux-64": "1.20260317.1",
2585
- "@cloudflare/workerd-linux-arm64": "1.20260317.1",
2586
- "@cloudflare/workerd-windows-64": "1.20260317.1"
2513
+ "@cloudflare/workerd-darwin-64": "1.20260515.1",
2514
+ "@cloudflare/workerd-darwin-arm64": "1.20260515.1",
2515
+ "@cloudflare/workerd-linux-64": "1.20260515.1",
2516
+ "@cloudflare/workerd-linux-arm64": "1.20260515.1",
2517
+ "@cloudflare/workerd-windows-64": "1.20260515.1"
2587
2518
  }
2588
2519
  },
2589
2520
  "node_modules/wrangler": {
2590
- "version": "4.76.0",
2591
- "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.76.0.tgz",
2592
- "integrity": "sha512-Wan+CU5a0tu4HIxGOrzjNbkmxCT27HUmzrMj6kc7aoAnjSLv50Ggcn2Ant7wNQrD6xW3g31phKupZJgTZ8wZfQ==",
2521
+ "version": "4.92.0",
2522
+ "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.92.0.tgz",
2523
+ "integrity": "sha512-/DKpQHPxkuZbQsO9dFW2700VTD/4DSZMHjy92fO/frNoDRi/zQsFCAd2ONCV6TGqcUoXcP3D8Bo2gj/L4M0qQQ==",
2593
2524
  "dev": true,
2594
2525
  "license": "MIT OR Apache-2.0",
2595
2526
  "dependencies": {
2596
- "@cloudflare/kv-asset-handler": "0.4.2",
2597
- "@cloudflare/unenv-preset": "2.16.0",
2527
+ "@cloudflare/kv-asset-handler": "0.5.0",
2528
+ "@cloudflare/unenv-preset": "2.16.1",
2598
2529
  "blake3-wasm": "2.1.5",
2599
2530
  "esbuild": "0.27.3",
2600
- "miniflare": "4.20260317.1",
2531
+ "miniflare": "4.20260515.0",
2601
2532
  "path-to-regexp": "6.3.0",
2602
2533
  "unenv": "2.0.0-rc.24",
2603
- "workerd": "1.20260317.1"
2534
+ "workerd": "1.20260515.1"
2604
2535
  },
2605
2536
  "bin": {
2606
2537
  "wrangler": "bin/wrangler.js",
2607
2538
  "wrangler2": "bin/wrangler.js"
2608
2539
  },
2609
2540
  "engines": {
2610
- "node": ">=20.0.0"
2541
+ "node": ">=22.0.0"
2611
2542
  },
2612
2543
  "optionalDependencies": {
2613
2544
  "fsevents": "~2.3.2"
2614
2545
  },
2615
2546
  "peerDependencies": {
2616
- "@cloudflare/workers-types": "^4.20260317.1"
2547
+ "@cloudflare/workers-types": "^4.20260515.1"
2617
2548
  },
2618
2549
  "peerDependenciesMeta": {
2619
2550
  "@cloudflare/workers-types": {
@@ -10,9 +10,10 @@
10
10
  "typecheck": "tsc --noEmit"
11
11
  },
12
12
  "devDependencies": {
13
- "@cloudflare/workers-types": "^4.20250313.0",
13
+ "@cloudflare/workers-types": "^4.20260515.1",
14
+ "@types/node": "^22.19.19",
14
15
  "typescript": "^5.7.0",
15
16
  "vitest": "^3.0.0",
16
- "wrangler": "^4.0.0"
17
+ "wrangler": "^4.92.0"
17
18
  }
18
19
  }
@@ -1,5 +1,6 @@
1
1
  // src/mcp-handler.ts
2
2
  import type { JsonRpcRequest, JsonRpcResponse, ToolInfo, AggregatedResponse } from "./types";
3
+ import { PROJECT_WEBSITE_URL, RELAY_SERVER_ICONS } from "./types";
3
4
 
4
5
  /** Interface that the Durable Object will implement */
5
6
  export interface RelayStub {
@@ -26,7 +27,12 @@ export async function handleMcpRequest(
26
27
  if (method === "initialize") {
27
28
  return jsonRpcResult(id, {
28
29
  protocolVersion: "2025-03-26",
29
- serverInfo: { name: "unifi-mcp-relay", version: "1.0.0" },
30
+ serverInfo: {
31
+ name: "unifi-mcp-relay",
32
+ version: "1.0.0",
33
+ websiteUrl: PROJECT_WEBSITE_URL,
34
+ icons: RELAY_SERVER_ICONS,
35
+ },
30
36
  capabilities: { tools: {} },
31
37
  });
32
38
  }
@@ -3,6 +3,7 @@ import { DurableObject } from "cloudflare:workers";
3
3
  import type { RelayStub } from "./mcp-handler";
4
4
  import { handleMcpRequest } from "./mcp-handler";
5
5
  import { hashToken, generateToken, extractBearerToken } from "./auth";
6
+ import { toolInputSchema, toolServerOrigin } from "./tool-info";
6
7
  import type {
7
8
  Env,
8
9
  ToolInfo,
@@ -29,8 +30,11 @@ const MAX_LOCATION_NAME_LENGTH = 128;
29
30
 
30
31
  const META_TOOL_INDEX: ToolInfo = {
31
32
  name: "unifi_tool_index",
33
+ title: "UniFi Tool Index",
32
34
  description:
33
- "List all available UniFi tools with descriptions and categories. " +
35
+ "Discover available UniFi tools. Returns names and descriptions by default. " +
36
+ "Use 'category' to filter by area (e.g. clients, firewall, devices), " +
37
+ "'search' for keyword matching, or 'include_schemas' for full parameter schemas. " +
34
38
  "Use this to discover tools before calling unifi_execute.",
35
39
  inputSchema: {
36
40
  type: "object",
@@ -43,6 +47,13 @@ const META_TOOL_INDEX: ToolInfo = {
43
47
  type: "string",
44
48
  description: "Optional search term to filter tools by name or description",
45
49
  },
50
+ include_schemas: {
51
+ type: "boolean",
52
+ description:
53
+ "Include full input schemas per tool. Defaults to false. " +
54
+ "Set true with a category or search filter to get parameter details for specific tools.",
55
+ default: false,
56
+ },
46
57
  },
47
58
  },
48
59
  annotations: {
@@ -55,6 +66,7 @@ const META_TOOL_INDEX: ToolInfo = {
55
66
 
56
67
  const META_TOOL_EXECUTE: ToolInfo = {
57
68
  name: "unifi_execute",
69
+ title: "UniFi Execute",
58
70
  description:
59
71
  "Execute a UniFi tool by name. Use unifi_tool_index to discover available tools first. " +
60
72
  "Pass the tool name and its arguments.",
@@ -84,6 +96,7 @@ const META_TOOL_EXECUTE: ToolInfo = {
84
96
 
85
97
  const META_TOOL_BATCH: ToolInfo = {
86
98
  name: "unifi_batch",
99
+ title: "UniFi Batch",
87
100
  description:
88
101
  "Execute multiple UniFi tools in a single request. Each call is an object with " +
89
102
  "'tool' and 'arguments'. Results are returned in order.",
@@ -116,6 +129,7 @@ const META_TOOL_BATCH: ToolInfo = {
116
129
 
117
130
  const META_TOOL_LOCATION_TIMELINE: ToolInfo = {
118
131
  name: "unifi_location_timeline",
132
+ title: "UniFi Location Timeline",
119
133
  description:
120
134
  "Query events across all connected UniFi products (Network, Protect, Access) " +
121
135
  "and return a unified, time-sorted timeline. Correlates network events, camera " +
@@ -195,6 +209,7 @@ export class RelayObject extends DurableObject<Env> implements RelayStub {
195
209
  CREATE TABLE IF NOT EXISTS location_tools (
196
210
  location_id TEXT NOT NULL,
197
211
  tool_name TEXT NOT NULL,
212
+ title TEXT,
198
213
  description TEXT NOT NULL DEFAULT '',
199
214
  input_schema TEXT,
200
215
  annotations TEXT,
@@ -203,6 +218,15 @@ export class RelayObject extends DurableObject<Env> implements RelayStub {
203
218
  FOREIGN KEY (location_id) REFERENCES locations(location_id)
204
219
  );
205
220
  `);
221
+ this.ensureLocationToolsTitleColumn();
222
+ }
223
+
224
+ private ensureLocationToolsTitleColumn(): void {
225
+ const columns = this.ctx.storage.sql.exec(`PRAGMA table_info(location_tools)`).toArray();
226
+ const hasTitle = columns.some((column) => column.name === "title");
227
+ if (!hasTitle) {
228
+ this.ctx.storage.sql.exec(`ALTER TABLE location_tools ADD COLUMN title TEXT`);
229
+ }
206
230
  }
207
231
 
208
232
  // -------------------------------------------------------------------------
@@ -228,7 +252,7 @@ export class RelayObject extends DurableObject<Env> implements RelayStub {
228
252
 
229
253
  // Load all tools grouped by location
230
254
  const rows = this.ctx.storage.sql.exec(
231
- `SELECT lt.location_id, lt.tool_name, lt.description, lt.input_schema, lt.annotations, lt.server_origin
255
+ `SELECT lt.location_id, lt.tool_name, lt.title, lt.description, lt.input_schema, lt.annotations, lt.server_origin
232
256
  FROM location_tools lt
233
257
  JOIN locations l ON lt.location_id = l.location_id`,
234
258
  ).toArray();
@@ -240,6 +264,9 @@ export class RelayObject extends DurableObject<Env> implements RelayStub {
240
264
  description: (row.description as string) || "",
241
265
  };
242
266
 
267
+ if (row.title) {
268
+ tool.title = row.title as string;
269
+ }
243
270
  if (row.input_schema) {
244
271
  try {
245
272
  tool.inputSchema = JSON.parse(row.input_schema as string);
@@ -585,14 +612,15 @@ export class RelayObject extends DurableObject<Env> implements RelayStub {
585
612
 
586
613
  for (const tool of msg.tools) {
587
614
  this.ctx.storage.sql.exec(
588
- `INSERT INTO location_tools (location_id, tool_name, description, input_schema, annotations, server_origin)
589
- VALUES (?, ?, ?, ?, ?, ?)`,
615
+ `INSERT INTO location_tools (location_id, tool_name, title, description, input_schema, annotations, server_origin)
616
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
590
617
  locationId,
591
618
  tool.name,
619
+ tool.title || null,
592
620
  tool.description || "",
593
- tool.inputSchema ? JSON.stringify(tool.inputSchema) : null,
621
+ toolInputSchema(tool) ? JSON.stringify(toolInputSchema(tool)) : null,
594
622
  tool.annotations ? JSON.stringify(tool.annotations) : null,
595
- tool.serverOrigin || null,
623
+ toolServerOrigin(tool) || null,
596
624
  );
597
625
  }
598
626
 
@@ -661,14 +689,15 @@ export class RelayObject extends DurableObject<Env> implements RelayStub {
661
689
 
662
690
  for (const tool of msg.tools) {
663
691
  this.ctx.storage.sql.exec(
664
- `INSERT INTO location_tools (location_id, tool_name, description, input_schema, annotations, server_origin)
665
- VALUES (?, ?, ?, ?, ?, ?)`,
692
+ `INSERT INTO location_tools (location_id, tool_name, title, description, input_schema, annotations, server_origin)
693
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
666
694
  locationId,
667
695
  tool.name,
696
+ tool.title || null,
668
697
  tool.description || "",
669
- tool.inputSchema ? JSON.stringify(tool.inputSchema) : null,
698
+ toolInputSchema(tool) ? JSON.stringify(toolInputSchema(tool)) : null,
670
699
  tool.annotations ? JSON.stringify(tool.annotations) : null,
671
- tool.serverOrigin || null,
700
+ toolServerOrigin(tool) || null,
672
701
  );
673
702
  }
674
703
 
@@ -743,13 +772,16 @@ export class RelayObject extends DurableObject<Env> implements RelayStub {
743
772
  private handleToolIndex(args: Record<string, unknown>): Record<string, unknown> {
744
773
  const category = args.category as string | undefined;
745
774
  const search = args.search as string | undefined;
775
+ const includeSchemas = Boolean(args.include_schemas);
746
776
 
747
777
  // Build tool list with location metadata
748
778
  const toolEntries: Array<{
749
779
  name: string;
780
+ title?: string;
750
781
  description: string;
751
782
  locations: string[];
752
783
  annotations?: ToolAnnotations;
784
+ inputSchema?: Record<string, unknown>;
753
785
  }> = [];
754
786
 
755
787
  const seen = new Set<string>();
@@ -757,12 +789,19 @@ export class RelayObject extends DurableObject<Env> implements RelayStub {
757
789
  for (const tool of tools) {
758
790
  if (!seen.has(tool.name)) {
759
791
  seen.add(tool.name);
760
- toolEntries.push({
792
+ const entry: (typeof toolEntries)[number] = {
761
793
  name: tool.name,
762
794
  description: tool.description,
763
795
  locations: this.toolToLocations.get(tool.name) || [locationId],
764
796
  annotations: tool.annotations,
765
- });
797
+ };
798
+ if (tool.title) {
799
+ entry.title = tool.title;
800
+ }
801
+ if (includeSchemas && tool.inputSchema) {
802
+ entry.inputSchema = tool.inputSchema;
803
+ }
804
+ toolEntries.push(entry);
766
805
  }
767
806
  }
768
807
  }
@@ -0,0 +1,9 @@
1
+ import type { ToolInfo } from "./types";
2
+
3
+ export function toolInputSchema(tool: ToolInfo): Record<string, unknown> | undefined {
4
+ return tool.inputSchema ?? tool.input_schema;
5
+ }
6
+
7
+ export function toolServerOrigin(tool: ToolInfo): string | undefined {
8
+ return tool.serverOrigin ?? tool.server_origin;
9
+ }
@@ -9,6 +9,24 @@ export const PROTOCOL_VERSION = 1;
9
9
  export const TOOL_CALL_TIMEOUT_MS = 30_000;
10
10
  export const HEARTBEAT_INTERVAL_MS = 30_000;
11
11
  export const HEARTBEAT_ACK_TIMEOUT_MS = 10_000;
12
+ export const PROJECT_WEBSITE_URL = "https://github.com/sirkirby/unifi-mcp";
13
+
14
+ export interface IconInfo {
15
+ src: string;
16
+ mimeType?: string;
17
+ sizes?: string[];
18
+ }
19
+
20
+ const RELAY_ICON_SRC =
21
+ "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20192%20192%22%3E%3Crect%20width%3D%22192%22%20height%3D%22192%22%20rx%3D%2238%22%20fill%3D%22%23111827%22%2F%3E%3Cpath%20d%3D%22M54%2096h84M96%2054v84%22%20stroke%3D%22%2338bdf8%22%20stroke-width%3D%2214%22%20stroke-linecap%3D%22round%22%2F%3E%3Ccircle%20cx%3D%2254%22%20cy%3D%2296%22%20r%3D%2218%22%20fill%3D%22%2322c55e%22%2F%3E%3Ccircle%20cx%3D%22138%22%20cy%3D%2296%22%20r%3D%2218%22%20fill%3D%22%2322c55e%22%2F%3E%3Ccircle%20cx%3D%2296%22%20cy%3D%2254%22%20r%3D%2218%22%20fill%3D%22%2322c55e%22%2F%3E%3Ccircle%20cx%3D%2296%22%20cy%3D%22138%22%20r%3D%2218%22%20fill%3D%22%2322c55e%22%2F%3E%3C%2Fsvg%3E";
22
+
23
+ export const RELAY_SERVER_ICONS: IconInfo[] = [
24
+ {
25
+ src: RELAY_ICON_SRC,
26
+ mimeType: "image/svg+xml",
27
+ sizes: ["192x192"],
28
+ },
29
+ ];
12
30
 
13
31
  // ---------------------------------------------------------------------------
14
32
  // Environment (Cloudflare bindings)
@@ -34,10 +52,13 @@ export interface ToolAnnotations {
34
52
 
35
53
  export interface ToolInfo {
36
54
  name: string;
55
+ title?: string;
37
56
  description: string;
38
57
  inputSchema?: Record<string, unknown>;
58
+ input_schema?: Record<string, unknown>;
39
59
  annotations?: ToolAnnotations;
40
60
  serverOrigin?: string;
61
+ server_origin?: string;
41
62
  }
42
63
 
43
64
  // ---------------------------------------------------------------------------