lattice-mcp 1.1.0 → 1.1.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 (4) hide show
  1. package/AGENTS.md +40 -21
  2. package/README.md +169 -71
  3. package/index.js +22 -7
  4. package/package.json +3 -2
package/AGENTS.md CHANGED
@@ -32,8 +32,9 @@ Those live in [`lattice-api`](https://github.com/aidenappl/lattice-api) and
32
32
  - **Runtime:** Node ≥18 (needs global `fetch` and `AbortSignal.timeout`). `"type": "module"` —
33
33
  ESM only, top-level `await` is used at the bottom of `index.js`.
34
34
  - **`@modelcontextprotocol/sdk` ^1.29.0** — `McpServer` + `StdioServerTransport`.
35
- - **`zod`** — argument schemas. Supplied transitively by the MCP SDK; it is *not* a declared
36
- dependency, which is a latent fragility (see Rules & guardrails).
35
+ - **`zod` ^4.4.3** — argument schemas. Declared explicitly as of **1.1.1** (it was previously
36
+ only resolved transitively through the MCP SDK, a latent fragility); the range matches the
37
+ version the SDK resolves.
37
38
  - No build step, no bundler, no tests, no lint config. `node --check index.js` is the only
38
39
  static gate.
39
40
 
@@ -90,9 +91,12 @@ and supply the real token.
90
91
  the noun (`lattice_list_database_instances`). The prefix is what disambiguates these from
91
92
  `monitor_*` and `forta_*` tools in a shared client.
92
93
  - **`api(method, path, params, body)`** — `params` become query string entries (undefined/null
93
- are dropped), `body` is JSON-encoded. Errors are swallowed and returned as
94
- `{success:false, error_message}` so a network failure surfaces as tool output rather than a
95
- transport crash.
94
+ are dropped), `body` is JSON-encoded. Failures are returned as tool output, never thrown, so
95
+ they surface to the agent rather than crashing the transport. A network-level failure returns
96
+ `{success:false, error_message: "API request failed: …"}`; a response that arrives but is not
97
+ JSON (e.g. a 502/504 HTML page from the TLS proxy) returns
98
+ `{success:false, error_code: <http status>, error_message: "API returned a non-JSON response …"}`
99
+ with a truncated body — bodies are never logged, only returned, since they can carry secrets.
96
100
  - **`body(obj)`** strips `undefined` keys. **Always use it on PUT/PATCH tools.** The API treats
97
101
  a present-but-null field as an explicit clear, so forwarding raw `{...fields}` on an update
98
102
  would wipe every field the caller didn't pass.
@@ -122,28 +126,42 @@ exits immediately if either is missing. In practice these come from the `env` bl
122
126
 
123
127
  **Tool groups**, in file order:
124
128
 
129
+ The counts below sum to **125**, matching the header and `grep -c 'server.tool(' index.js`. The
130
+ first six rows are the original, pre-`1.1.0` tools (registered top-of-file with no banner comment);
131
+ every bolded row corresponds to a `// ───` banner group and matches its exact in-file name.
132
+
125
133
  | Group | Tools | Notes |
126
134
  |-------|-------|-------|
127
135
  | Overview & health | 2 | `lattice_overview`, `lattice_health` |
128
136
  | Workers | 3 + 4 actions | list/get/metrics; reboot, upgrade, stop-all, start-all |
129
- | Stacks | 2 + 4 actions | list/get; deploy, restart, stop, start, update |
130
- | Containers | 5 + 8 actions | get/list/logs/lifecycle/metrics; start, stop, restart, kill, pause, unpause, remove, recreate |
131
- | Deployments | 4 | list/get/logs, rollback, approve |
132
- | Audit & API tokens | 4 | audit log; token list/create/delete |
133
- | **Database instances** | **11** | CRUD, `lattice_database_action`, credentials, snapshots, restore |
134
- | **Backup destinations** | **6** | CRUD + test |
135
- | **Registries** | **8** | CRUD, test, test-inline, repositories, tags |
136
- | **Discovery & diagnostics** | **7** | search, anomalies, fleet-metrics, versions, refresh-versions, container metrics, self |
137
- | **Stacks — compose & tokens** | **13** | create/delete, compose update/sync/import, export/import, save-template, deploy tokens |
138
- | **Containers — definition CRUD** | **3** | create/update/delete definitions |
139
- | **Workers — resources** | **16** | worker CRUD, tokens, volumes, networks, force-remove |
140
- | **Global env vars / templates / webhooks** | **12** | |
141
- | **Users & instance config** | **12** | users, SSO, SMTP, notification prefs |
137
+ | Stacks | 2 + 5 actions | list/get; deploy, restart, stop, start, update |
138
+ | Containers | 4 + 8 actions | list/get/logs/lifecycle; start, stop, restart, kill, pause, unpause, remove, recreate. (`lattice_get_container_metrics` is *not* here — it lives under **Discovery & diagnostics**.) |
139
+ | Deployments | 4 | list/get/logs, rollback. (`lattice_approve_deployment` is *not* here — it lives under **Stacks — lifecycle, compose & deploy tokens**.) |
140
+ | Instance self-update | 2 | `lattice_update_api`, `lattice_update_web` — tell the API/web container to pull its latest image and redeploy itself |
141
+ | Audit & API tokens | 4 | `lattice_get_audit_log`; API token list/create/delete |
142
+ | **Database instances** | **11** | CRUD, `lattice_database_action` (start/stop/restart/remove enum), `lattice_get_database_credentials`, snapshot list/create/restore/delete |
143
+ | **Backup destinations** | **6** | list/get/create/update/delete + `lattice_test_backup_destination` |
144
+ | **Registries** | **8** | list/create/update/delete, `lattice_test_registry`, `lattice_test_registry_inline`, `lattice_list_registry_repositories`, `lattice_list_registry_tags` |
145
+ | **Discovery & diagnostics** | **7** | `lattice_search`, `lattice_get_anomalies`, `lattice_get_fleet_metrics`, `lattice_get_versions`, `lattice_refresh_versions`, `lattice_get_container_metrics`, `lattice_get_self` |
146
+ | **Stacks — lifecycle, compose & deploy tokens** | **13** | create/delete, `lattice_get_stack_containers`, compose update/sync/import, export/import, `lattice_save_stack_as_template`, deploy-token list/create/delete, `lattice_approve_deployment` |
147
+ | **Containers — definition CRUD** | **3** | `lattice_create_container`, `lattice_update_container`, `lattice_delete_container` |
148
+ | **Workers — registration, tokens, volumes, networks** | **16** | worker create/update/delete, `lattice_get_worker_container_stats`, worker-token ×3, volume ×3, worker-network ×3, `lattice_list_all_networks`, `lattice_delete_network`, `lattice_force_remove_container` |
149
+ | **Global env vars, templates, webhooks** | **12** | env-var CRUD (4), template list/create/delete (3), webhook list/create/update/delete/test (5) |
150
+ | **Users & instance configuration** | **11** | user CRUD (4); SSO get/update (2); SMTP get/update/test (3); notification-prefs get/update (2) |
142
151
 
143
152
  Bolded groups were added in **1.1.0**, closing a gap where the MCP had drifted roughly two
144
153
  months behind `lattice-api` — database instances shipped in the API in May 2026 and were
145
154
  entirely unreachable from the MCP until then.
146
155
 
156
+ **1.1.1** is a bug-fix release (no tool count change). It corrects three request-shape/route bugs
157
+ verified against `lattice-api` handlers: `lattice_get_self` now calls `GET /auth/self` (the old
158
+ `GET /admin/self` route never existed and always 404'd — the endpoint is `HandleAuthSelf` on the
159
+ bearer-authed `/auth` subrouter); `lattice_force_remove_container` now sends `{container_name}` in
160
+ the JSON body (`HandleForceRemoveContainer` reads it there, not from query params, so the old call
161
+ always 400'd); and `lattice_test_backup_destination` now marks `worker_id` as **required** (a query
162
+ param the handler 400s without — the test dispatches over the worker's WebSocket). It also hardens
163
+ `api()` against non-JSON responses (see *How code is written here*) and declares `zod` explicitly.
164
+
147
165
  **Consolidations.** Where `lattice-api` exposes several paths served by one handler, this repo
148
166
  exposes one tool with an enum rather than N tools. `lattice_database_action` covers
149
167
  `/start`, `/stop`, `/restart` and `/remove`. Worker actions are the historical exception — they
@@ -187,9 +205,10 @@ predate this convention and remain separate tools.
187
205
  - **Do not break tool names.** They are a public contract: renaming one silently breaks any
188
206
  saved workflow or prompt that referenced it. Add a new tool and deprecate in the description
189
207
  instead.
190
- - **`zod` is used but not declared** in `package.json` — it resolves transitively through the
191
- MCP SDK. If the SDK ever drops or hoists it differently, every tool schema breaks at startup.
192
- Adding it as an explicit dependency is the correct fix; do it in a standalone change.
208
+ - **`zod` is declared explicitly** in `package.json` (`^4.4.3`) as of **1.1.1**. It used to
209
+ resolve only transitively through the MCP SDK, which meant a SDK change that dropped or hoisted
210
+ it differently would break every tool schema at startup. Keep the declared range aligned with
211
+ the version the SDK actually resolves (check `package-lock.json`).
193
212
  - **Keep destructive descriptions honest.** If a tool destroys data, the description must say so
194
213
  and name the safer alternative.
195
214
  - Publishing is outward-facing and effectively irreversible (npm unpublish is restricted after
package/README.md CHANGED
@@ -1,18 +1,55 @@
1
1
  # lattice-mcp
2
2
 
3
- MCP server for the [Lattice](https://github.com/aidenappl/lattice-api) container orchestration platform. Gives Claude Code direct access to manage workers, stacks, containers, and deployments.
3
+ Model Context Protocol server for [Lattice](https://github.com/aidenappl/lattice-api), the container orchestration platform that runs every `appleby.cloud` service. Gives Claude Code direct, typed access to workers, stacks, containers, deployments, databases, registries, networks, volumes and instance configuration.
4
4
 
5
- ## Quick Start
5
+ > **appleby.cloud platform** · MCP server · published to npm as `lattice-mcp` · consumed via `npx -y lattice-mcp`
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ `lattice-mcp` is a single-file Node ESM program (`index.js`) that speaks MCP over stdio and translates tool calls into HTTP requests against the `lattice-api` admin surface. It exposes **125 typed tools** and holds no business logic, caching or state of its own — every behaviour (pagination, validation, side effects) comes from `lattice-api`.
12
+
13
+ Once configured, ask Claude Code things like:
14
+
15
+ - "What's the status of all stacks?"
16
+ - "Show me logs for the forta-api container"
17
+ - "Which containers are unhealthy?" (`lattice_get_anomalies` is the best first call)
18
+ - "Deploy stack 5" / "Rollback the last deployment on stack 12"
19
+ - "What image tags can I deploy from the registry?"
20
+
21
+ ## Role in the appleby.cloud ecosystem
22
+
23
+ | Repo | Relationship |
24
+ |------|--------------|
25
+ | [`lattice-api`](https://github.com/aidenappl/lattice-api) | The API this wraps — its route table is the source of truth for tool coverage. |
26
+ | [`lattice-web`](https://github.com/aidenappl/lattice-web) | Next.js dashboard over the same API. |
27
+ | [`lattice-runner`](https://github.com/aidenappl/lattice-runner) | Agent on each worker VM; WebSocket back to `lattice-api`. |
28
+ | `monitor-mcp` / `forta-mcp` / `keyring-mcp` / `openbucket-mcp` | Sibling MCP servers, same single-file structure. |
29
+
30
+ ## Tech stack
31
+
32
+ - **Node ≥18** (needs global `fetch` and `AbortSignal.timeout`), ESM (`"type": "module"`).
33
+ - **`@modelcontextprotocol/sdk` ^1.29.0** — `McpServer` + `StdioServerTransport`.
34
+ - **`zod` ^4.4.3** for argument schemas (a declared dependency as of 1.1.1).
35
+ - No build step, no bundler. `node --check index.js` is the only static gate.
36
+
37
+ ## Getting started
38
+
39
+ ### Prerequisites
40
+
41
+ - Node ≥18.
42
+ - A Lattice API URL and API token. Generate a token from the Lattice web dashboard under **Settings > API Tokens**.
43
+
44
+ ### Setup
45
+
46
+ Quickest — interactive setup writes the `lattice` block into `~/.mcp.json`:
6
47
 
7
48
  ```bash
8
49
  npx lattice-mcp --setup
9
50
  ```
10
51
 
11
- This prompts for your Lattice API URL and API token, writes the config to `~/.mcp.json`, and you're ready to go. Restart Claude Code after setup.
12
-
13
- ## Manual Setup
14
-
15
- Add to `~/.mcp.json`:
52
+ Or configure it manually in `~/.mcp.json`:
16
53
 
17
54
  ```json
18
55
  {
@@ -29,21 +66,41 @@ Add to `~/.mcp.json`:
29
66
  }
30
67
  ```
31
68
 
32
- Generate an API token from the Lattice web dashboard under **Settings > API Tokens**.
69
+ Restart Claude Code after setup so the new server and tools are picked up.
70
+
71
+ ### Environment variables
72
+
73
+ | Variable | Required | Description |
74
+ |----------|----------|-------------|
75
+ | `LATTICE_API_URL` | Yes | Lattice API base URL |
76
+ | `LATTICE_API_TOKEN` | Yes | Bearer token for authentication (sent on every request) |
77
+
78
+ ## Development
79
+
80
+ | Command | What it does |
81
+ |---------|--------------|
82
+ | `node index.js --setup` | Interactive setup — writes the `lattice` block into `~/.mcp.json` |
83
+ | `npm install` | Install dependencies (not vendored) |
84
+ | `node --check index.js` | Syntax gate — the only static check that exists |
85
+ | `LATTICE_API_URL=… LATTICE_API_TOKEN=… node index.js` | Run the server on stdio |
86
+ | `grep -c 'server.tool(' index.js` | Confirm the tool count (should be 125) |
87
+ | `npm publish` | Publish to npm — **this is deployment** (requires 2FA passkey from an interactive terminal) |
33
88
 
34
89
  ## Tools
35
90
 
36
- ### Overview & Health
91
+ All 125 tools, grouped as they appear in `index.js`. ⚠️ marks destructive tools; their descriptions state the blast radius.
92
+
93
+ ### Overview & health
37
94
  | Tool | Description |
38
95
  |------|-------------|
39
- | `lattice_overview` | Fleet overview — worker counts, stack counts, failed stacks, CPU/memory |
96
+ | `lattice_overview` | Fleet overview — worker/stack/container counts, failed stacks, CPU/memory |
40
97
  | `lattice_health` | API health and database connectivity |
41
98
 
42
99
  ### Workers
43
100
  | Tool | Description |
44
101
  |------|-------------|
45
- | `lattice_list_workers` | List workers with status, IP, versions |
46
- | `lattice_get_worker` | Detailed worker info |
102
+ | `lattice_list_workers` | List workers with status, IP, versions, heartbeat |
103
+ | `lattice_get_worker` | Detailed worker info including metrics |
47
104
  | `lattice_get_worker_metrics` | CPU, memory, disk, network metrics |
48
105
  | `lattice_reboot_worker` | Reboot a worker machine |
49
106
  | `lattice_upgrade_worker` | Upgrade worker runner to latest |
@@ -55,11 +112,11 @@ Generate an API token from the Lattice web dashboard under **Settings > API Toke
55
112
  |------|-------------|
56
113
  | `lattice_list_stacks` | List stacks with status and worker assignment |
57
114
  | `lattice_get_stack` | Full stack details including compose YAML |
58
- | `lattice_update_stack` | Update stack configuration |
59
115
  | `lattice_deploy_stack` | Deploy a stack (all or specific containers) |
60
116
  | `lattice_restart_stack` | Restart all containers in a stack |
61
117
  | `lattice_stop_stack` | Stop all containers in a stack |
62
118
  | `lattice_start_stack` | Start all containers in a stack |
119
+ | `lattice_update_stack` | Update stack configuration |
63
120
 
64
121
  ### Containers
65
122
  | Tool | Description |
@@ -74,8 +131,8 @@ Generate an API token from the Lattice web dashboard under **Settings > API Toke
74
131
  | `lattice_kill_container` | Force kill a container |
75
132
  | `lattice_pause_container` | Pause a running container |
76
133
  | `lattice_unpause_container` | Unpause a paused container |
77
- | `lattice_remove_container` | Remove a container |
78
- | `lattice_recreate_container` | Remove and recreate a container |
134
+ | `lattice_remove_container` | Remove a container ⚠️ |
135
+ | `lattice_recreate_container` | Remove and recreate a container ⚠️ |
79
136
 
80
137
  ### Deployments
81
138
  | Tool | Description |
@@ -83,36 +140,21 @@ Generate an API token from the Lattice web dashboard under **Settings > API Toke
83
140
  | `lattice_list_deployments` | List deployments with status and timing |
84
141
  | `lattice_get_deployment` | Deployment details with container-level status |
85
142
  | `lattice_get_deployment_logs` | Pull, create, start, swap events with timing |
86
- | `lattice_rollback_deployment` | Rollback to previous state |
143
+ | `lattice_rollback_deployment` | Rollback to previous state ⚠️ |
144
+
145
+ ### Instance self-update
146
+ | Tool | Description |
147
+ |------|-------------|
148
+ | `lattice_update_api` | Trigger the Lattice API container to self-update |
149
+ | `lattice_update_web` | Trigger the Lattice web container to update |
87
150
 
88
- ### System
151
+ ### Audit & API tokens
89
152
  | Tool | Description |
90
153
  |------|-------------|
91
- | `lattice_get_audit_log` | Recent audit log entries |
92
- | `lattice_update_api` | Trigger API self-update |
93
- | `lattice_update_web` | Trigger web container update |
154
+ | `lattice_get_audit_log` | Recent audit log entries (who did what, when) |
94
155
  | `lattice_list_api_tokens` | List API tokens |
95
156
  | `lattice_create_api_token` | Create a new API token |
96
- | `lattice_delete_api_token` | Delete an API token |
97
-
98
- ## Example Prompts
99
-
100
- - "What's the status of all stacks?"
101
- - "Show me logs for the forta-api container"
102
- - "Deploy stack 5"
103
- - "Which containers are unhealthy?"
104
- - "Rollback the last deployment on stack 12"
105
-
106
- ## Environment Variables
107
-
108
- | Variable | Required | Description |
109
- |----------|----------|-------------|
110
- | `LATTICE_API_URL` | Yes | Lattice API base URL |
111
- | `LATTICE_API_TOKEN` | Yes | API token for authentication |
112
-
113
- ## License
114
-
115
- MIT
157
+ | `lattice_delete_api_token` | Delete an API token ⚠️ |
116
158
 
117
159
  ### Database instances
118
160
  | Tool | Description |
@@ -132,18 +174,22 @@ MIT
132
174
  ### Backup destinations
133
175
  | Tool | Description |
134
176
  |------|-------------|
135
- | `lattice_list_backup_destinations` / `lattice_get_backup_destination` | Inventory |
136
- | `lattice_create_backup_destination` / `lattice_update_backup_destination` | Manage destinations |
137
- | `lattice_delete_backup_destination` | Delete ⚠️ |
138
- | `lattice_test_backup_destination` | Test connectivity without writing a backup |
177
+ | `lattice_list_backup_destinations` | List backup destinations |
178
+ | `lattice_get_backup_destination` | One destination's configuration |
179
+ | `lattice_create_backup_destination` | Create a destination |
180
+ | `lattice_update_backup_destination` | Update a destination |
181
+ | `lattice_delete_backup_destination` | Delete a destination ⚠️ |
182
+ | `lattice_test_backup_destination` | Test connectivity without writing a backup (requires a connected `worker_id`) |
139
183
 
140
184
  ### Registries
141
185
  | Tool | Description |
142
186
  |------|-------------|
143
- | `lattice_list_registries` | Configured registries |
144
- | `lattice_create_registry` / `lattice_update_registry` | Manage registries |
145
- | `lattice_delete_registry` | Delete ⚠️ |
146
- | `lattice_test_registry` / `lattice_test_registry_inline` | Test stored or unsaved credentials |
187
+ | `lattice_list_registries` | Configured container registries |
188
+ | `lattice_create_registry` | Add a registry |
189
+ | `lattice_update_registry` | Update a registry |
190
+ | `lattice_delete_registry` | Delete a registry ⚠️ |
191
+ | `lattice_test_registry` | Test a saved registry's stored credentials |
192
+ | `lattice_test_registry_inline` | Test registry credentials before saving |
147
193
  | `lattice_list_registry_repositories` | What images exist |
148
194
  | `lattice_list_registry_tags` | **What versions are deployable** |
149
195
 
@@ -153,53 +199,105 @@ MIT
153
199
  | `lattice_search` | Search workers, stacks and containers in one call |
154
200
  | `lattice_get_anomalies` | **Restart loops, unhealthy containers, offline workers — best first call** |
155
201
  | `lattice_get_fleet_metrics` | Aggregated fleet CPU/memory/disk/network |
156
- | `lattice_get_versions` / `lattice_refresh_versions` | Runner versions and what's outdated |
202
+ | `lattice_get_versions` | Runner versions and what's outdated |
203
+ | `lattice_refresh_versions` | Re-poll every worker for its current runner version |
157
204
  | `lattice_get_container_metrics` | Per-container metrics over time |
158
205
  | `lattice_get_self` | Which user the token authenticates as |
159
206
 
160
- ### Stacks — compose, export & deploy tokens
207
+ ### Stacks — lifecycle, compose & deploy tokens
161
208
  | Tool | Description |
162
209
  |------|-------------|
163
- | `lattice_create_stack` / `lattice_delete_stack` | Stack lifecycle ⚠️ |
210
+ | `lattice_create_stack` | Create an empty stack |
211
+ | `lattice_delete_stack` | Delete a stack and all its containers ⚠️ |
164
212
  | `lattice_get_stack_containers` | Containers in a stack |
165
- | `lattice_update_stack_compose` / `lattice_sync_stack_compose` | Compose YAML management |
213
+ | `lattice_update_stack_compose` | Replace a stack's compose YAML |
214
+ | `lattice_sync_stack_compose` | Reconcile container records against stored compose YAML |
166
215
  | `lattice_import_compose` | Create a stack from compose YAML |
167
- | `lattice_export_stack` / `lattice_import_stack_export` | Portable stack backup/restore |
216
+ | `lattice_export_stack` | Export a stack's full definition as portable JSON |
217
+ | `lattice_import_stack_export` | Recreate a stack from an export document |
168
218
  | `lattice_save_stack_as_template` | Save a stack as a reusable template |
169
219
  | `lattice_list_deploy_tokens` | CI deploy tokens — `last_used_at` shows whether CI reaches Lattice |
170
- | `lattice_create_deploy_token` / `lattice_delete_deploy_token` | Manage CI deploy tokens ⚠️ |
220
+ | `lattice_create_deploy_token` | Create a CI deploy token |
221
+ | `lattice_delete_deploy_token` | Delete a CI deploy token ⚠️ |
171
222
  | `lattice_approve_deployment` | Approve a deployment awaiting approval |
172
223
 
173
224
  ### Container definitions
174
225
  | Tool | Description |
175
226
  |------|-------------|
176
- | `lattice_create_container` / `lattice_update_container` | Manage container definitions |
177
- | `lattice_delete_container` | Delete definition and container ⚠️ |
227
+ | `lattice_create_container` | Add a container definition to a stack |
228
+ | `lattice_update_container` | Update a container definition |
229
+ | `lattice_delete_container` | Delete definition and its running container ⚠️ |
178
230
 
179
- ### Workers — resources
231
+ ### Workers — registration, tokens, volumes, networks
180
232
  | Tool | Description |
181
233
  |------|-------------|
182
- | `lattice_create_worker` / `lattice_update_worker` / `lattice_delete_worker` | Worker registration ⚠️ |
183
- | `lattice_get_worker_container_stats` | Live per-container stats |
184
- | `lattice_list_worker_tokens` / `lattice_create_worker_token` / `lattice_delete_worker_token` | Runner registration tokens ⚠️ |
185
- | `lattice_list_worker_volumes` / `lattice_create_worker_volume` / `lattice_delete_worker_volume` | Docker volumes ⚠️ |
186
- | `lattice_list_worker_networks` / `lattice_create_worker_network` / `lattice_delete_worker_network` | Docker networks ⚠️ |
187
- | `lattice_list_all_networks` / `lattice_delete_network` | Fleet-wide networks ⚠️ |
234
+ | `lattice_create_worker` | Register a worker |
235
+ | `lattice_update_worker` | Update a worker's name, hostname, IP, status, labels |
236
+ | `lattice_delete_worker` | Delete a worker ⚠️ |
237
+ | `lattice_get_worker_container_stats` | Live per-container stats from one worker |
238
+ | `lattice_list_worker_tokens` | Worker registration tokens |
239
+ | `lattice_create_worker_token` | Create a registration token for a worker |
240
+ | `lattice_delete_worker_token` | Delete a worker token ⚠️ |
241
+ | `lattice_list_worker_volumes` | Docker volumes on a worker |
242
+ | `lattice_create_worker_volume` | Create a Docker volume on a worker |
243
+ | `lattice_delete_worker_volume` | Delete a Docker volume ⚠️ |
244
+ | `lattice_list_worker_networks` | Docker networks on a worker |
245
+ | `lattice_create_worker_network` | Create a Docker network on a worker |
246
+ | `lattice_delete_worker_network` | Delete a Docker network ⚠️ |
247
+ | `lattice_list_all_networks` | Every tracked network across the fleet |
248
+ | `lattice_delete_network` | Delete a tracked network by Lattice ID ⚠️ |
188
249
  | `lattice_force_remove_container` | Force-remove a wedged container ⚠️ |
189
250
 
190
251
  ### Env vars, templates & webhooks
191
252
  | Tool | Description |
192
253
  |------|-------------|
193
- | `lattice_list_env_vars` / `lattice_create_env_var` / `lattice_update_env_var` / `lattice_delete_env_var` | Global `${VAR}` interpolation values ⚠️ |
194
- | `lattice_list_templates` / `lattice_create_template` / `lattice_delete_template` | Stack templates ⚠️ |
195
- | `lattice_list_webhooks` / `lattice_create_webhook` / `lattice_update_webhook` / `lattice_delete_webhook` / `lattice_test_webhook` | Outbound event webhooks ⚠️ |
254
+ | `lattice_list_env_vars` | Global `${VAR}` interpolation values (secrets masked) |
255
+ | `lattice_create_env_var` | Create a global environment variable |
256
+ | `lattice_update_env_var` | Update a global environment variable |
257
+ | `lattice_delete_env_var` | Delete a global environment variable ⚠️ |
258
+ | `lattice_list_templates` | Saved stack templates |
259
+ | `lattice_create_template` | Create a stack template |
260
+ | `lattice_delete_template` | Delete a stack template ⚠️ |
261
+ | `lattice_list_webhooks` | Outbound event webhooks |
262
+ | `lattice_create_webhook` | Create an outbound webhook |
263
+ | `lattice_update_webhook` | Update a webhook |
264
+ | `lattice_delete_webhook` | Delete a webhook ⚠️ |
265
+ | `lattice_test_webhook` | Send a test payload to a webhook |
196
266
 
197
267
  ### Users & instance configuration
198
268
  | Tool | Description |
199
269
  |------|-------------|
200
- | `lattice_list_users` / `lattice_create_user` / `lattice_update_user` / `lattice_delete_user` | User management ⚠️ |
201
- | `lattice_get_sso_config` / `lattice_update_sso_config` | Forta SSO ⚠️ |
202
- | `lattice_get_smtp_config` / `lattice_update_smtp_config` / `lattice_test_smtp` | Alert email |
203
- | `lattice_get_notification_prefs` / `lattice_update_notification_prefs` | Per-event notification prefs |
270
+ | `lattice_list_users` | List Lattice users with roles and status |
271
+ | `lattice_create_user` | Create a local Lattice user |
272
+ | `lattice_update_user` | Update a user's name, role or active flag ⚠️ |
273
+ | `lattice_delete_user` | Delete a Lattice user ⚠️ |
274
+ | `lattice_get_sso_config` | Get the Forta SSO configuration |
275
+ | `lattice_update_sso_config` | Update SSO config ⚠️ (can lock out SSO users) |
276
+ | `lattice_get_smtp_config` | Get the SMTP configuration |
277
+ | `lattice_update_smtp_config` | Update the SMTP configuration |
278
+ | `lattice_test_smtp` | Send a test email with the saved SMTP config |
279
+ | `lattice_get_notification_prefs` | Per-event notification preferences |
280
+ | `lattice_update_notification_prefs` | Update notification preferences |
281
+
282
+ ## Project structure
283
+
284
+ Everything lives in one file:
285
+
286
+ | Path | Role |
287
+ |------|------|
288
+ | `index.js` | The whole server: `--setup` flow, config read, `api()` HTTP helper, `text()`/`body()` helpers, all 125 `server.tool(...)` registrations, transport connect. |
289
+ | `package.json` | npm metadata; `bin.lattice-mcp` → `index.js`. |
290
+ | `AGENTS.md` | Contributor/agent guide — conventions, handler contracts, verification. |
291
+ | `README.md` | This file. |
292
+
293
+ ## Deployment
204
294
 
205
- ⚠️ = destructive. Tool descriptions state the blast radius.
295
+ Published to npm as `lattice-mcp` (public). Consumers run `npx -y lattice-mcp`, which resolves the latest published version — so **publishing is deployment**, and a running MCP server must be restarted to pick up a new version. `npm publish` requires 2FA via passkey from an interactive terminal.
296
+
297
+ ## Contributing & further reading
298
+
299
+ Read [`AGENTS.md`](./AGENTS.md) before changing code — it documents the one-shape tool pattern, the `body()`/`api()` helpers, the "read the handler before adding a tool" rule, and the verification steps. Related repos: [`lattice-api`](https://github.com/aidenappl/lattice-api), [`lattice-web`](https://github.com/aidenappl/lattice-web), [`lattice-runner`](https://github.com/aidenappl/lattice-runner).
300
+
301
+ ## License
302
+
303
+ MIT
package/index.js CHANGED
@@ -77,12 +77,27 @@ async function api(method, path, params, body) {
77
77
  opts.headers["Content-Type"] = "application/json";
78
78
  opts.body = JSON.stringify(body);
79
79
  }
80
+ let res;
80
81
  try {
81
- const res = await fetch(url.toString(), opts);
82
- return await res.json();
82
+ res = await fetch(url.toString(), opts);
83
83
  } catch (err) {
84
+ // Network-level failure (DNS, connection refused, timeout) — no response at all.
84
85
  return { success: false, error: err.message, error_message: `API request failed: ${err.message}` };
85
86
  }
87
+ // Response arrived. Read the body once as text, then try to parse it as JSON.
88
+ // On parse failure surface the HTTP status and a truncated text body rather than
89
+ // an opaque parse error — a 502/504 HTML page from the TLS proxy lands here.
90
+ // Never log bodies anywhere; responses can carry secrets.
91
+ const raw = await res.text();
92
+ try {
93
+ return JSON.parse(raw);
94
+ } catch {
95
+ return {
96
+ success: false,
97
+ error_code: res.status,
98
+ error_message: `API returned a non-JSON response (HTTP ${res.status})${raw ? `: ${raw.slice(0, 500)}` : ""}`,
99
+ };
100
+ }
86
101
  }
87
102
 
88
103
  function text(data) {
@@ -99,7 +114,7 @@ function body(obj) {
99
114
 
100
115
  const server = new McpServer({
101
116
  name: "lattice",
102
- version: "1.0.0",
117
+ version: "1.1.1",
103
118
  });
104
119
 
105
120
  // Overview
@@ -559,9 +574,9 @@ server.tool("lattice_delete_backup_destination", "Delete a backup destination. I
559
574
  return { content: text(res) };
560
575
  });
561
576
 
562
- server.tool("lattice_test_backup_destination", "Test connectivity and credentials for a backup destination without writing a real backup", {
577
+ server.tool("lattice_test_backup_destination", "Test connectivity and credentials for a backup destination without writing a real backup. The test is dispatched over the worker's WebSocket, so worker_id is required and that worker must be connected", {
563
578
  id: z.number().describe("Backup destination ID"),
564
- worker_id: z.number().optional().describe("Worker to run the test from"),
579
+ worker_id: z.number().describe("Worker to run the test from — required; the test runs on this worker over its WebSocket, so it must be connected"),
565
580
  }, async ({ id, worker_id }) => {
566
581
  const res = await api("POST", `/admin/backup-destinations/${id}/test`, { worker_id });
567
582
  return { content: text(res) };
@@ -682,7 +697,7 @@ server.tool("lattice_get_container_metrics", "Get a single container's CPU, memo
682
697
  });
683
698
 
684
699
  server.tool("lattice_get_self", "Get the user this API token authenticates as, including role", {}, async () => {
685
- const res = await api("GET", "/admin/self");
700
+ const res = await api("GET", "/auth/self");
686
701
  return { content: text(res) };
687
702
  });
688
703
 
@@ -974,7 +989,7 @@ server.tool("lattice_force_remove_container", "Force-remove a container on a wor
974
989
  id: z.number().describe("Worker ID"),
975
990
  name: z.string().describe("Container name to force-remove"),
976
991
  }, async ({ id, name }) => {
977
- const res = await api("POST", `/admin/workers/${id}/force-remove`, { name });
992
+ const res = await api("POST", `/admin/workers/${id}/force-remove`, null, { container_name: name });
978
993
  return { content: text(res) };
979
994
  });
980
995
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lattice-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "MCP server for Lattice container orchestration platform",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -24,6 +24,7 @@
24
24
  "url": "git+https://github.com/aidenappl/lattice-mcp.git"
25
25
  },
26
26
  "dependencies": {
27
- "@modelcontextprotocol/sdk": "^1.29.0"
27
+ "@modelcontextprotocol/sdk": "^1.29.0",
28
+ "zod": "^4.4.3"
28
29
  }
29
30
  }