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/AGENT_GUIDE.md +298 -0
- package/LICENSE +21 -0
- package/PROTOCOL.md +650 -0
- package/README.md +81 -0
- package/dist/config.js +71 -0
- package/dist/connection.js +728 -0
- package/dist/index.js +285 -0
- package/mcpb/manifest.json +76 -0
- package/package.json +55 -0
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
|
+
}
|