crestron-mcp 1.8.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 ADDED
@@ -0,0 +1,81 @@
1
+ # CrestronMCP client
2
+
3
+ Control a Crestron 4-Series AV system from Claude, in natural language. This is the
4
+ client half of [CrestronMCP](https://solutionav.com.au/crestron-mcp/): an MCP server
5
+ that connects Claude (Desktop or Code) to a processor running the CrestronMCP modules,
6
+ exposing the system as MCP tools over stdio. It speaks the CrestronMCP text protocol
7
+ (see [`PROTOCOL.md`](PROTOCOL.md)) over TCP, with secure-key + TLS authentication.
8
+
9
+ The client is **free**. Controlling a processor requires that processor to be licensed
10
+ (or on a free trial). See [Licensing](#licensing). One processor licence is AUD $249
11
+ (inc GST); each processor also gets three free 1-week trials. Get a licence at
12
+ <https://solutionav.com.au/crestron-mcp/>.
13
+
14
+ ## Install
15
+
16
+ ### Claude Desktop (recommended)
17
+ Download `crestron-mcp.mcpb` from <https://solutionav.com.au/crestron-mcp/> and open it
18
+ (or Settings → Extensions → Install). Enter the processor's address and its secure key
19
+ (shown on the MCP Server Config module's `Key` output); the port defaults to `50794`.
20
+
21
+ ### Claude Code / other MCP hosts
22
+ No download needed. Run it straight from npm:
23
+
24
+ ```bash
25
+ claude mcp add crestron \
26
+ --env CRESTRON_HOST=10.0.1.38 \
27
+ --env CRESTRON_KEY=<the processor's secure key> \
28
+ -- npx -y crestron-mcp
29
+ ```
30
+
31
+ ## Configuration
32
+
33
+ Resolved low-to-high: `config.json` next to the entry, environment variables, then CLI
34
+ args (`<host> [port]`).
35
+
36
+ | Env | Meaning |
37
+ | --- | --- |
38
+ | `CRESTRON_HOST` | processor IP / hostname (required) |
39
+ | `CRESTRON_PORT` | TCP port (default `50794`) |
40
+ | `CRESTRON_KEY` | secure key (mode 2); enables TLS + authentication |
41
+ | `CRESTRON_AUTH` | password (mode 1 only) |
42
+ | `CRESTRON_TLS` | force TLS without a key |
43
+
44
+ ## Tools
45
+
46
+ `discover_crestron_system`, `list_crestron_rooms`, `list_crestron_devices`,
47
+ `query_crestron_device`, `get_crestron_time`, `control_crestron_device`,
48
+ `set_crestron_devices`, `pulse_crestron_device`, `ramp_crestron_device`,
49
+ `cancel_crestron_device`, `get_room_status`, `activate_crestron_license`,
50
+ `get_crestron_license_status`, `start_crestron_trial`.
51
+
52
+ See [`AGENT_GUIDE.md`](AGENT_GUIDE.md) for how an assistant should use them (timing,
53
+ scenes, ramps, nudge-not-nag licensing etiquette).
54
+
55
+ ## Licensing
56
+
57
+ The processor must be licensed before it accepts control or query commands. If it isn't,
58
+ every tool returns guidance that includes the processor's **activation code (its MAC)**.
59
+ Two ways forward, both in chat:
60
+
61
+ - **Free trial**: `start_crestron_trial` (no payment; up to 3 × 1 week per processor).
62
+ - **Buy**: get a key for that MAC at <https://solutionav.com.au/crestron-mcp/>, paste it
63
+ in chat, and the assistant calls `activate_crestron_license`.
64
+
65
+ The licence is stored **on the processor** (bound to its MAC), so it persists across
66
+ reboots and covers every client. A purchased key only works on that one processor, so it's
67
+ safe to receive in chat.
68
+
69
+ ## Develop
70
+
71
+ ```bash
72
+ npm install
73
+ npm run build # tsc -> dist/
74
+ npm start # node dist/index.js
75
+ npm run mcpb # build the Claude Desktop .mcpb (needs bun)
76
+ ```
77
+
78
+ ## License
79
+
80
+ MIT. See [`LICENSE`](LICENSE). (The client is open; the product is the per-processor
81
+ licence on the box.)
package/dist/config.js ADDED
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Resolve host/port/auth/key/tls. Precedence (low to high): config.json next to
3
+ * the binary, then environment variables, then CLI args. Mirrors the Python
4
+ * client's load_config().
5
+ *
6
+ * CRESTRON_HOST / CRESTRON_PORT
7
+ * CRESTRON_AUTH - mode-1 shared password
8
+ * CRESTRON_KEY - mode-2 HMAC key (implies TLS)
9
+ * CRESTRON_TLS - force TLS even without a key
10
+ *
11
+ * Licensing is NOT configured here: it lives on the processor, installed once via the
12
+ * activate_crestron_license tool (the LLM walks the user through it in chat).
13
+ */
14
+ import { readFileSync } from "node:fs";
15
+ import { fileURLToPath } from "node:url";
16
+ import { dirname, join } from "node:path";
17
+ function truthy(v) {
18
+ return ["1", "true", "yes", "on"].includes(String(v).trim().toLowerCase());
19
+ }
20
+ export function loadConfig(argv = process.argv.slice(2)) {
21
+ let host = "192.168.1.100";
22
+ let port = 50794;
23
+ let auth = "";
24
+ let key = "";
25
+ let tls = false;
26
+ // config.json next to the build output, or one level up (project root / next
27
+ // to a packaged binary).
28
+ const here = dirname(fileURLToPath(import.meta.url));
29
+ for (const path of [join(here, "config.json"), join(here, "..", "config.json")]) {
30
+ try {
31
+ const cfg = JSON.parse(readFileSync(path, "utf8"));
32
+ const c = cfg.crestron ?? {};
33
+ if (c.host !== undefined)
34
+ host = String(c.host);
35
+ if (c.port !== undefined)
36
+ port = Number(c.port);
37
+ if (c.auth !== undefined)
38
+ auth = String(c.auth);
39
+ if (c.key !== undefined)
40
+ key = String(c.key);
41
+ if (c.tls !== undefined)
42
+ tls = Boolean(c.tls);
43
+ break;
44
+ }
45
+ catch {
46
+ /* not found / unreadable - fall through to env + args */
47
+ }
48
+ }
49
+ const env = process.env;
50
+ // Empty strings are ignored for host/port (a packaged .mcpb may inject blank
51
+ // env vars for unset optional fields; CRESTRON_PORT="" must not become 0).
52
+ if (env.CRESTRON_HOST)
53
+ host = env.CRESTRON_HOST;
54
+ if (env.CRESTRON_PORT) {
55
+ const n = Number(env.CRESTRON_PORT);
56
+ if (Number.isFinite(n) && n > 0)
57
+ port = n;
58
+ }
59
+ // Credentials: a defined-but-empty value legitimately means "no credential".
60
+ if (env.CRESTRON_AUTH !== undefined)
61
+ auth = env.CRESTRON_AUTH;
62
+ if (env.CRESTRON_KEY !== undefined)
63
+ key = env.CRESTRON_KEY;
64
+ if (env.CRESTRON_TLS !== undefined)
65
+ tls = truthy(env.CRESTRON_TLS);
66
+ if (argv[0])
67
+ host = argv[0];
68
+ if (argv[1])
69
+ port = Number(argv[1]);
70
+ return { host, port, auth, key, tls };
71
+ }