@thinremote/thinr-cli 1.0.2 → 1.1.0-alpha.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 (46) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/README.md +362 -87
  3. package/bin/thinr.js +51 -11
  4. package/commands/device/_shared.js +48 -0
  5. package/commands/device/console.js +15 -0
  6. package/commands/device/exec.js +76 -0
  7. package/commands/device/index.js +31 -0
  8. package/commands/device/list.js +43 -0
  9. package/commands/device/property.js +50 -0
  10. package/commands/device/proxy.js +42 -0
  11. package/commands/device/resource.js +51 -0
  12. package/commands/device/status.js +26 -0
  13. package/commands/device/update.js +53 -0
  14. package/commands/logout.js +12 -5
  15. package/commands/product.js +146 -177
  16. package/commands/profile.js +105 -0
  17. package/lib/api.js +54 -31
  18. package/lib/auth.js +225 -309
  19. package/lib/config.js +176 -45
  20. package/lib/console.js +87 -108
  21. package/lib/device-api.js +257 -0
  22. package/lib/devices.js +69 -0
  23. package/lib/errors.js +53 -0
  24. package/lib/mcp/helpers.js +34 -0
  25. package/lib/mcp/registry.js +32 -0
  26. package/lib/mcp/server.js +63 -0
  27. package/lib/mcp/tools-devices.js +194 -0
  28. package/lib/mcp/tools-exec.js +213 -0
  29. package/lib/mcp/tools-filesystem.js +163 -0
  30. package/lib/mcp/tools-products.js +232 -0
  31. package/lib/mcp/tools-profiles.js +30 -0
  32. package/lib/mcp/tools-properties.js +79 -0
  33. package/lib/mcp/tools-resources.js +76 -0
  34. package/lib/mcp/tools-scripts.js +172 -0
  35. package/lib/monitoring.js +72 -0
  36. package/lib/output.js +114 -0
  37. package/lib/product.js +373 -0
  38. package/lib/property.js +41 -57
  39. package/lib/proxy.js +58 -90
  40. package/lib/resource.js +106 -72
  41. package/lib/status.js +7 -33
  42. package/lib/storage.js +128 -0
  43. package/package.json +68 -37
  44. package/.github/workflows/publish.yml +0 -79
  45. package/commands/device.js +0 -268
  46. package/lib/device.js +0 -76
package/CHANGELOG.md CHANGED
@@ -1,5 +1,94 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0] - 2026-04-16
4
+
5
+ Major release: MCP server for AI clients, subcommand-first CLI, JSON
6
+ output, multi-profile configuration, product-level fan-outs, and a
7
+ consistent error taxonomy across both interfaces.
8
+
9
+ ### Added
10
+
11
+ - **MCP server** (`thinr mcp`) exposing ~26 typed tools over stdio for AI
12
+ clients (Claude Code, Claude Desktop, and any other MCP host). Covers
13
+ discovery, shell `exec`, filesystem (`read`/`write`/`ls`/`mkdir`/`delete`/
14
+ `move`), typed `resource` calls, properties, scripts (device- and
15
+ product-level), products, monitoring and agent updates, plus a profiles
16
+ lookup. MCP errors carry the same `{ code }` taxonomy as the CLI JSON
17
+ mode (exposed on `_meta`) so clients can pattern-match without parsing
18
+ human strings.
19
+ - **Subcommand-first CLI UX**: `thinr device <action> <deviceId>` /
20
+ `thinr product <action> <productId>` replaces the previous top-level
21
+ flag shape. Matches `kubectl` / `git` idioms.
22
+ - `thinr device list [pattern]` with case-insensitive regex filtering on
23
+ the server side (replaces the old `thinr devices`).
24
+ - `thinr device exec <deviceId> <command...>` with streaming stdout/stderr,
25
+ SIGINT cancellation, `--json` envelope, and `--legacy` one-shot fallback
26
+ for older agents.
27
+ - `thinr device update check|apply` for managing agent upgrades.
28
+ - `thinr device resource` with per-resource input/output schema
29
+ introspection, and `thinr product resource` / `thinr product property`
30
+ for fleet fan-outs.
31
+ - **Multi-profile configuration store** (`~/.config/thinr-cli/config.json`):
32
+ one profile per server, selected via `--profile`, the `THINR_PROFILE`
33
+ env var, or a per-call MCP parameter. Legacy single-file configs are
34
+ detected and migrated in-memory on read.
35
+ - `thinr profile list|current|use|delete` for managing configured
36
+ environments.
37
+ - `--json` / `-j` output envelope on every data-producing command with a
38
+ stable `{ code }` taxonomy: `not_configured`, `not_found`,
39
+ `unauthorized`, `server_error`, `network_error`, `input_error`,
40
+ `timeout`, `cancelled`, `error`.
41
+ - `-u, --user` admin impersonation flag plumbed through every
42
+ device-scoped operation (CLI + MCP).
43
+ - `THINR_INSECURE=1` env var to accept self-signed TLS certificates
44
+ against non-localhost hosts (localhost / 127.0.0.1 are always
45
+ accepted).
46
+ - Device property setter via PUT (`setDeviceProperty` in the CLI,
47
+ `thinr_property_set` in MCP).
48
+ - `thinr_device_info` MCP tool surfaces server-side product, asset group
49
+ and description alongside the agent's own `system_info`.
50
+ - Refresh-token flow on 401s: the CLI transparently swaps expired access
51
+ tokens for a fresh one when a `refresh_token` is on record, and fails
52
+ cleanly (with an actionable `unauthorized` error) when it isn't.
53
+
54
+ ### Changed
55
+
56
+ - **MCP server internals**: tool schema and handler now paired in a
57
+ registry, so they cannot drift apart. Tools grouped by area under
58
+ `lib/mcp/tools-*.js`, with `server.js` reduced to a thin bootstrap.
59
+ - **CLI `device` command** split into one file per subcommand under
60
+ `commands/device/` (previously a single 17 KB module).
61
+ - `lib/device.js` renamed to `lib/devices.js` (plural: operations on the
62
+ fleet), paired with the low-level `lib/device-api.js`.
63
+ - Interactive handlers (auth / console / proxy) no longer call
64
+ `process.exit` from `lib/*`; the CLI entry owns the exit code.
65
+ - Centralised API error classification in `lib/errors.js` — one
66
+ `{ message, code, status }` contract consumed by both CLI JSON mode
67
+ and MCP `_meta`.
68
+ - Re-thrown auth errors chain the original `cause` for better
69
+ debugging.
70
+ - Bad `-i key=value` values surface as a clean Commander
71
+ `InvalidArgumentError` instead of a stack trace.
72
+ - Node.js engine requirement bumped to **`>=18`**; `.nvmrc` pinned to 20.
73
+
74
+ ### Removed
75
+
76
+ - `thinr devices` (use `thinr device list` instead).
77
+ - `thinr device mcp <deviceId>` (use `thinr mcp` with per-call `device`
78
+ parameter).
79
+ - `thinr device env` launcher.
80
+
81
+ ### Tooling
82
+
83
+ - ESLint 10 (flat config) + Prettier 3 with `eslint-config-prettier`
84
+ bridge. Scripts: `npm run lint` / `lint:fix` / `format` /
85
+ `format:check`.
86
+ - TypeScript `checkJs` via per-file `// @ts-check` opt-in; 21 files
87
+ under `lib/mcp/` and `commands/device/` pass type-check today. Script:
88
+ `npm run typecheck`.
89
+ - Unit tests with `node --test` (zero deps): MCP registry invariants +
90
+ error helpers. Script: `npm test`.
91
+
3
92
  ## [1.0.2] - 2025-07-21
4
93
 
5
94
  ### Fixed
@@ -31,6 +120,7 @@
31
120
  - Property listing and retrieval
32
121
  - Device resource listing and execution
33
122
 
123
+ [1.1.0]: https://github.com/Thin-Remote/thinr-cli/compare/1.0.2...1.1.0
34
124
  [1.0.2]: https://github.com/Thin-Remote/thinr-cli/compare/1.0.1...1.0.2
35
125
  [1.0.1]: https://github.com/Thin-Remote/thinr-cli/compare/1.0.0...1.0.1
36
126
  [1.0.0]: https://github.com/Thin-Remote/thinr-cli/tag/1.0.0
package/README.md CHANGED
@@ -1,18 +1,45 @@
1
- # Thinr - ThinRemote CLI
1
+ # Thinr — ThinRemote CLI
2
2
 
3
3
  <p align="center">
4
- <img src='https://s3.us-east-1.amazonaws.com/thinremote.io.files/share-image.svg' alt="Thinger n8n node">
4
+ <img src='https://s3.us-east-1.amazonaws.com/thinremote.io.files/share-image.svg' alt="Thinger">
5
5
  </p>
6
6
 
7
- A modern command-line interface for managing remote devices through the ThinRemote platform.
8
-
9
- ## Features
10
-
11
- - **Remote Terminal**: Connect to a device's terminal and interact with it directly
12
- - **TCP/Web Proxy**: Create proxies to remote devices, either for TCP connections or web interfaces
13
- - **Device Status**: Check connection status and statistics for a device
14
- - **Secure Authentication**: Support for both username/password and token-based authentication
15
- - **Modern UI**: Clean and intuitive command-line interface
7
+ A command-line tool for managing remote IoT devices on the ThinRemote
8
+ platform, and an MCP server that lets AI assistants drive those
9
+ devices programmatically.
10
+
11
+ Two operating modes:
12
+
13
+ - **CLI** — interactive or scripted device management from a shell.
14
+ - **MCP server** — a stdio [Model Context Protocol][mcp] server that
15
+ exposes the same operations as typed tools for AI clients like
16
+ Claude Code.
17
+
18
+ Both modes share the same auth, profiles, and device API layer, so
19
+ you configure the tool once and use it in whichever mode fits the
20
+ task at hand.
21
+
22
+ [mcp]: https://modelcontextprotocol.io/
23
+
24
+ ## Table of contents
25
+
26
+ - [Installation](#installation)
27
+ - [Initial setup](#initial-setup)
28
+ - [Global options and environment variables](#global-options-and-environment-variables)
29
+ - [Part 1 — CLI](#part-1--cli)
30
+ - [JSON output](#json-output)
31
+ - [`thinr device <action> [<deviceId>]`](#thinr-device-action-deviceid)
32
+ - [`thinr product <action> <productId>`](#thinr-product-action-productid)
33
+ - [`thinr profile`](#thinr-profile)
34
+ - [`thinr logout`](#thinr-logout)
35
+ - [Part 2 — MCP server](#part-2--mcp-server)
36
+ - [Starting the server](#starting-the-server)
37
+ - [Tool catalog](#tool-catalog)
38
+ - [Per-call controls](#per-call-controls)
39
+ - [Integrating with Claude Code](#integrating-with-claude-code)
40
+ - [Profiles and multi-account use](#profiles-and-multi-account-use)
41
+ - [Development](#development)
42
+ - [License](#license)
16
43
 
17
44
  ## Installation
18
45
 
@@ -22,7 +49,7 @@ A modern command-line interface for managing remote devices through the ThinRemo
22
49
  npm install -g @thinremote/thinr-cli
23
50
  ```
24
51
 
25
- ### From Source
52
+ ### From source
26
53
 
27
54
  ```bash
28
55
  git clone https://github.com/Thin-Remote/thinr-cli.git
@@ -31,174 +58,422 @@ npm install
31
58
  npm link
32
59
  ```
33
60
 
34
- ## Usage
61
+ Requires Node.js ≥ 18.
35
62
 
36
- ### Initial Setup
63
+ ## Initial setup
37
64
 
38
- Run the CLI without any parameters to set up:
65
+ Run the CLI once with no arguments to authenticate:
39
66
 
40
67
  ```bash
41
68
  thinr
42
69
  ```
43
70
 
44
- You'll be prompted to authenticate either with username/password or with an existing token.
71
+ You'll be prompted for the ThinRemote server, and can authenticate
72
+ with either username/password (OAuth 2.0 device flow) or a pre-issued
73
+ token. On success a profile is created at
74
+ `~/.config/thinr-cli/config.json`, named after the server hostname,
75
+ and marked as the default.
76
+
77
+ Every later command reads that profile automatically. Logging into a
78
+ second server creates a second profile side-by-side; see
79
+ [Profiles and multi-account use](#profiles-and-multi-account-use).
80
+
81
+ ## Global options and environment variables
82
+
83
+ These apply to every subcommand:
84
+
85
+ | Flag | Env var | Meaning |
86
+ | ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
87
+ | `--profile <name>` | `THINR_PROFILE` | Use a non-default profile for this invocation. |
88
+ | `-u, --user <name>` | — | Admin impersonation: act on behalf of another user (requires admin privileges on the server). |
89
+ | — | `THINR_INSECURE=1` | Accept self-signed TLS certificates globally. Localhost / 127.0.0.1 are always accepted; this flag extends the relaxation to any host, useful for LAN dev servers. |
90
+
91
+ ---
92
+
93
+ ## Part 1 — CLI
94
+
95
+ All device- and product-scoped actions follow a **subcommand-first**
96
+ shape: the action comes before the target id, matching patterns like
97
+ `kubectl describe pod <name>` or `git remote add <name> <url>`.
98
+
99
+ ```
100
+ thinr device <action> <deviceId> [args…] [options]
101
+ thinr product <action> <productId> [args…] [options]
102
+ ```
103
+
104
+ ### JSON output
105
+
106
+ Every data-producing command accepts `-j, --json` and writes a single
107
+ envelope to stdout so scripts can pipe it to `jq`:
108
+
109
+ ```json
110
+ { "ok": true, "data": <payload> }
111
+ { "ok": false, "error": { "message": "...", "code": "<code>" } }
112
+ ```
113
+
114
+ Process exit codes always reflect success (`0`) or failure (non-zero),
115
+ independently of the envelope.
116
+
117
+ Emitted error `code` values:
118
+
119
+ | code | meaning |
120
+ | ---------------- | ------------------------------------------------- |
121
+ | `not_configured` | CLI has no saved profile — run `thinr` to set up. |
122
+ | `not_found` | Device, property, resource or profile not found. |
123
+ | `unauthorized` | Token expired or insufficient permissions. |
124
+ | `server_error` | Non-success HTTP response from the server. |
125
+ | `network_error` | No response from the server. |
126
+ | `input_error` | Bad or missing CLI argument. |
127
+ | `timeout` | Command timed out on the device (`exec`). |
128
+ | `cancelled` | User interrupted with Ctrl+C (`exec`). |
129
+ | `error` | Fallback for anything uncategorised. |
130
+
131
+ Spinners and progress messages are suppressed in JSON mode so the
132
+ output is valid JSON without wrappers. Interactive commands
133
+ (`console`, `tcp`/`tls`/`http`) accept `--json` silently but
134
+ don't change their behaviour — they don't produce a discrete result.
135
+ `exec` buffers stdout/stderr when `--json` is set and emits a single
136
+ envelope on exit.
137
+
138
+ ### `thinr device <action> [<deviceId>]`
139
+
140
+ Most actions take a `<deviceId>` as their first positional. The `list`
141
+ action is the exception — it operates on the fleet, with an optional
142
+ `<pattern>` to filter results.
45
143
 
46
- ### Available Commands
144
+ #### `list`
47
145
 
48
- #### Device
146
+ List every device visible to the active profile, or filter with a
147
+ case-insensitive regex (matched against device id and name on the
148
+ server side):
49
149
 
50
- ##### Console
150
+ ```bash
151
+ thinr device list # every device
152
+ thinr device list supermicro # only ids/names containing "supermicro"
153
+ thinr device list "^revpi|jetson" # regex alternation works too
154
+ thinr device list --json # JSON envelope with the raw API records
155
+ ```
156
+
157
+ Combine with the global `-u, --user` flag for admin impersonation.
51
158
 
52
- Connect to a device's terminal:
159
+ #### `console`
160
+
161
+ Open an interactive terminal on the device:
53
162
 
54
163
  ```bash
55
- thinr device <deviceId> console
164
+ thinr device console <deviceId>
56
165
  ```
57
166
 
58
- ##### Proxy
167
+ #### Proxies — `tcp` / `tls` / `http`
59
168
 
60
- Create a TCP or HTTP proxy to a remote device:
169
+ Create a TCP, TLS, or HTTP proxy to a remote endpoint reachable from
170
+ the device:
61
171
 
62
172
  ```bash
63
- thinr device <deviceId> tcp <target> [options]
64
- thinr device <deviceId> tls <target> [options]
65
- thinr device <deviceId> http <taget> [options]
173
+ thinr device tcp <deviceId> [target] [options]
174
+ thinr device tls <deviceId> [target] [options]
175
+ thinr device http <deviceId> [target] [options]
66
176
  ```
67
177
 
68
- The target being the remote address and port on the device (defaults: tcp: 22, http: 80, tls: 443, and localhost for the target address).
178
+ `target` is the remote address and port on the device side. Defaults
179
+ when omitted: `tcp` → 22, `tls` → 443, `http` → 80, against
180
+ localhost.
69
181
 
70
182
  Options:
71
- - `-w, --web`: Create an HTTP proxy for web interface instead of TCP
72
- - `--no-open`: Do not open browser automatically (web mode only)
183
+
184
+ - `-p, --port <port>`: Local port to bind (default: random in 50000–51000)
185
+ - `--no-open`: Do not open the browser automatically (http only)
73
186
 
74
187
  Examples:
188
+
75
189
  ```bash
76
- # Create a TCP proxy to SSH
77
- thinr device RevPi20679 tcp 22
190
+ # TCP proxy to SSH
191
+ thinr device tcp RevPi20679 22
78
192
 
79
- # Create a proxy to a specific port with SSL
80
- thinr device RevPi20679 http 8080
193
+ # HTTP proxy to a specific port
194
+ thinr device http RevPi20679 8080
81
195
 
82
- # Create a proxy to a specific address
83
- thinr device RevPi20679 http http://192.168.1.45:8080
196
+ # HTTP proxy to a specific address behind the device
197
+ thinr device http RevPi20679 http://192.168.1.45:8080
84
198
  ```
85
199
 
86
- ##### Status
200
+ #### `status`
201
+
202
+ ```bash
203
+ thinr device status <deviceId> [-j, --json]
204
+ ```
205
+
206
+ Returns the device's connection stats (uptime, tx/rx, last-seen).
207
+
208
+ #### `property`
87
209
 
88
- Check the connection status of a device:
210
+ List every property of a device, or read one by id:
89
211
 
90
212
  ```bash
91
- thinr device <deviceId> status [options]
213
+ thinr device property <deviceId> # list
214
+ thinr device property <deviceId> <propertyId> [options] # read
92
215
  ```
93
216
 
94
217
  Options:
95
- - `-j, --json`: Output the status in JSON format
96
218
 
97
- ##### Property
219
+ - `-j, --json`: Output as JSON
220
+ - `-f, --field <path>`: Extract a sub-field via dot path (`-f data.value`)
98
221
 
99
- Retrieve the properties of a device:
222
+ #### `resource`
223
+
224
+ List the resources exposed by the device (with their `in`/`out`
225
+ schemas when advertised), or call one:
100
226
 
101
227
  ```bash
102
- thinr device <deviceId> property [options]
228
+ thinr device resource <deviceId> # list
229
+ thinr device resource <deviceId> <resourceId> [options] # call
103
230
  ```
104
231
 
105
- Retrieve a property from a device:
232
+ Options:
233
+
234
+ - `-i, --input <key=value>`: Resource input (repeatable)
235
+ - `-j, --json`: Output as JSON
236
+ - `-f, --field <path>`: Extract a sub-field from the result (dot path)
237
+
238
+ #### `exec`
239
+
240
+ Run a shell command on the device and stream stdout/stderr back:
106
241
 
107
242
  ```bash
108
- thinr device <deviceId> property <propertyId> [options]
243
+ thinr device exec <deviceId> "<command>" [options]
109
244
  ```
110
245
 
111
246
  Options:
112
- - `-j, --json`: Output the status in JSON format
113
- - `-f, --field`: Field to extract from the property or resource (e.g., -f data.value)
114
247
 
115
- ##### Resource
248
+ - `-j, --json`: Buffer output and emit a single envelope
249
+ `{stdout, stderr, exitCode}` on exit
250
+ - `--legacy`: Use the non-streaming one-shot API (older agents)
251
+
252
+ The process exits with the remote command's exit code.
116
253
 
117
- Get the available resources of a device:
254
+ #### `update`
255
+
256
+ Check for or apply an agent update on the device:
118
257
 
119
258
  ```bash
120
- thinr device <deviceId> resource [options]
259
+ thinr device update check <deviceId> [options]
260
+ thinr device update apply <deviceId> [options]
121
261
  ```
122
262
 
123
263
  Options:
124
- - `-j, --json`: Output the status in JSON format
125
264
 
126
- Execute a resource on a device:
265
+ - `--channel <name>`: Update channel (default: `latest`)
266
+ - `-j, --json`: Output as JSON
267
+
268
+ ### `thinr product <action> <productId>`
269
+
270
+ Fan out an operation across every device that belongs to a product.
271
+
272
+ #### `property`
127
273
 
128
274
  ```bash
129
- thinr device <deviceId> resource <resourceId> [options]
275
+ thinr product property <productId> <propertyId> [options]
130
276
  ```
131
277
 
132
278
  Options:
133
- - `-i, --input`: Input for the resource (e.g., -i param1=value1 -i param2=value2)
134
- - `-j, --json`: Output the status in JSON format
135
- - `-f, --field`: Field to extract from the property or resource (e.g., -f data.value)
136
279
 
137
- ##### Product
280
+ - `-j, --json`: Output as JSON. Emits one envelope with a `results[]`
281
+ array, one entry per device, each with its own `ok` flag.
282
+ - `-f, --field <path>`: Extract a sub-field from each property (dot path)
283
+ - `-a, --all`: Include offline devices (default: only active)
284
+ - `-g, --group <group>`: Filter devices by asset group
138
285
 
139
- Iterate over all devices of a specific product in order to retrieve a property or execute a resource:
286
+ #### `resource`
140
287
 
141
288
  ```bash
142
- thinr product <productId> property [options]
143
- thinr product <productId> resource [options]
289
+ thinr product resource <productId> <resource> [options]
144
290
  ```
145
291
 
146
292
  Options:
147
- - `-j, --json`: Output the status in JSON format
148
- - `-f, --field`: Field to extract from the property or resource (e.g., -f data.value)
149
293
 
150
- Options for executing a resource:
151
- - `-i, --input`: Input for the resource (e.g., -i param1=value1 -i param2=value2)
294
+ - `-j, --json`: Output as JSON (`results[]` array, same shape as above)
295
+ - `-f, --field <path>`: Extract a sub-field from each result (dot path)
296
+ - `-g, --group <group>`: Filter devices by asset group
297
+ - `-i, --input <key=value>`: Resource input (repeatable)
152
298
 
299
+ Offline devices are skipped for `resource` — the server would reject
300
+ the call anyway.
153
301
 
154
- ### Help
302
+ ### `thinr profile`
155
303
 
156
- Get help for any command:
304
+ Manage the profile store (see [Profiles and multi-account
305
+ use](#profiles-and-multi-account-use)):
157
306
 
158
307
  ```bash
159
- thinr --help
160
- thinr device --help
161
- thinr logout --help
308
+ thinr profile list [--json]
309
+ thinr profile current [--json]
310
+ thinr profile use <name> [--json]
311
+ thinr profile delete <name> [--json]
162
312
  ```
163
313
 
164
- ## Development
314
+ ### `thinr logout`
165
315
 
166
- The project structure is organized as follows:
316
+ Remove the active profile's credentials and config:
167
317
 
318
+ ```bash
319
+ thinr logout
168
320
  ```
169
- thinr/
170
- ├── bin/ # Main executable
171
- ├── lib/ # Core functionality
172
- ├── commands/ # Command implementations
173
- ├── package.json
174
- └── README.md
321
+
322
+ Other profiles are left untouched.
323
+
324
+ ---
325
+
326
+ ## Part 2 — MCP server
327
+
328
+ `thinr` ships an [MCP][mcp] server that exposes the CLI's device
329
+ operations as a set of typed tools. AI clients (Claude Code, Claude
330
+ Desktop, and any other MCP-compatible host) can then list devices,
331
+ run shell commands, read/write files, call resources, and manage
332
+ products through the same API the CLI uses — with stable schemas and
333
+ a consistent error contract.
334
+
335
+ ### Starting the server
336
+
337
+ ```bash
338
+ thinr mcp [-d, --device <deviceId>]
175
339
  ```
176
340
 
177
- ### Building
341
+ The server speaks [MCP over stdio][mcp-stdio]. `-d/--device` sets a
342
+ default device that tools can omit in each call; `--user` can be
343
+ passed through (via the global flag) to impersonate another account.
344
+
345
+ [mcp-stdio]: https://modelcontextprotocol.io/specification/server/transport#stdio
346
+
347
+ ### Tool catalog
348
+
349
+ Roughly 28 tools, grouped by capability. Every tool accepts optional
350
+ `device`, `user`, and `profile` arguments, so a single session can
351
+ target any device/account/environment without restart.
352
+
353
+ | Area | Tools |
354
+ | --------------------- | --------------------------------------------------------------------------------------------------------------------- |
355
+ | Discovery | `thinr_devices` (with optional `query` for regex filtering), `thinr_device_info`, `thinr_profiles` |
356
+ | Shell | `thinr_exec` (buffered), with streaming stdout/stderr |
357
+ | Filesystem | `thinr_read`, `thinr_write`, `thinr_ls`, `thinr_mkdir`, `thinr_delete`, `thinr_move` |
358
+ | Resources | `thinr_resource_list` (with `in`/`out` schemas), `thinr_resource_call` |
359
+ | Properties | `thinr_property_get`, `thinr_property_set` |
360
+ | Scripts (device) | `thinr_script_list`, `thinr_script_write`, `thinr_script_delete` |
361
+ | Monitoring and update | `thinr_monitoring`, `thinr_update` |
362
+ | Products | `thinr_products`, `thinr_product_delete`, `thinr_device_set_product` |
363
+ | Product scripts | `thinr_product_script_list`, `thinr_product_script_read`, `thinr_product_script_write`, `thinr_product_script_delete` |
364
+
365
+ Full input/output schemas are published via standard MCP
366
+ `list_tools`; the client will show them when you connect.
367
+
368
+ ### Per-call controls
369
+
370
+ The server starts with no per-session pin: every tool call must say
371
+ which device it's for, and may optionally override the account or
372
+ profile for that single call.
373
+
374
+ - **`device`** — required for any device-scoped tool. Use
375
+ `thinr_devices` (optionally with `query` to filter by regex) to
376
+ discover ids.
377
+ - **`user`** — optional admin impersonation; falls back to the
378
+ authenticated user of the active profile.
379
+ - **`profile`** — optional. Switches the active profile for that
380
+ single tool call so an agent can hop between prod and staging
381
+ without restarting the server. List the configured profiles with
382
+ `thinr_profiles`.
383
+
384
+ ### Integrating with Claude Code
385
+
386
+ Register the server once with the Claude Code CLI:
178
387
 
179
388
  ```bash
180
- npm install
181
- npm link
389
+ claude mcp add thinr -s user -- thinr mcp
182
390
  ```
183
391
 
184
- ### Testing
392
+ That's it — a single MCP server entry handles every device of the
393
+ active profile. Tool calls pass `device` (and optionally `user` /
394
+ `profile`) per call.
395
+
396
+ Other MCP hosts follow the same pattern — they just need the command
397
+ `thinr mcp` (optionally with `-d <deviceId>`) over stdio.
398
+
399
+ ---
400
+
401
+ ## Profiles and multi-account use
402
+
403
+ `~/.config/thinr-cli/config.json` stores any number of profiles:
404
+
405
+ ```json
406
+ {
407
+ "default": "perf.aws.thinger.io",
408
+ "profiles": {
409
+ "perf.aws.thinger.io": { "server": "...", "username": "...", "token": "..." },
410
+ "staging.local": { "server": "...", "username": "...", "token": "..." }
411
+ }
412
+ }
413
+ ```
414
+
415
+ The active profile is picked, in order:
416
+
417
+ 1. `--profile <name>` flag on the command line.
418
+ 2. `THINR_PROFILE` environment variable.
419
+ 3. The `default` field in the config file.
420
+ 4. The sole profile, if there's only one.
421
+
422
+ Logging in to a new server creates a new profile whose name is the
423
+ server hostname (so it never clobbers an existing one). Legacy
424
+ single-profile files from older versions of the CLI are detected on
425
+ read and migrated in-memory without a manual step.
426
+
427
+ Use `thinr profile use <name>` to change the persisted default, or
428
+ `--profile <name>` / `THINR_PROFILE=<name>` for one-off runs.
429
+
430
+ ## Development
431
+
432
+ ```
433
+ thinr-cli/
434
+ ├── bin/ # CLI entry point (thinr)
435
+ ├── commands/ # Commander subcommand modules
436
+ ├── lib/ # Core: api, auth, config, device-api, errors,
437
+ │ # output, mcp-server, proxy, console, …
438
+ ├── package.json
439
+ └── README.md
440
+ ```
441
+
442
+ Local development:
185
443
 
186
444
  ```bash
187
- npm test
445
+ npm install
446
+ npm link # makes `thinr` available on $PATH
447
+ node bin/thinr.js # or run directly without linking
188
448
  ```
189
449
 
450
+ There are no automated tests yet — contributions welcome.
451
+
190
452
  ## License
191
453
 
192
454
  <a href="http://opensource.org/">
193
455
  <img style="float: right;" width="100px" height="137px" src="https://opensource.org/wp-content/uploads/2009/06/OSI_Standard_Logo_0.svg">
194
456
  </a>
195
457
 
196
- The plugin is licensed under the [MIT License](http://opensource.org/licenses/MIT):
458
+ Released under the [MIT License](http://opensource.org/licenses/MIT).
197
459
 
198
460
  Copyright &copy; [Thinger.io](http://thinger.io)
199
461
 
200
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
201
-
202
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
203
-
204
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
462
+ Permission is hereby granted, free of charge, to any person obtaining
463
+ a copy of this software and associated documentation files (the
464
+ "Software"), to deal in the Software without restriction, including
465
+ without limitation the rights to use, copy, modify, merge, publish,
466
+ distribute, sublicense, and/or sell copies of the Software, and to
467
+ permit persons to whom the Software is furnished to do so, subject to
468
+ the following conditions:
469
+
470
+ The above copyright notice and this permission notice shall be
471
+ included in all copies or substantial portions of the Software.
472
+
473
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
474
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
475
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
476
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
477
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
478
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
479
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.